diff graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java @ 11195:4f52b08bd2f9

Truffle-DSL: Implemented specialization grouping for generic cases.
author Christian Humer <christian.humer@gmail.com>
date Thu, 01 Aug 2013 20:53:54 +0200
parents 4eb23800c907
children 3479ab380552
line wrap: on
line diff
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Thu Aug 01 20:53:05 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Thu Aug 01 20:53:54 2013 +0200
@@ -440,7 +440,7 @@
                 continue;
             }
 
-            CodeTree cast = createCast(parent, field, valueParam, guardedParam);
+            CodeTree cast = createCast(parent, field, valueParam, guardedParam.getTypeSystemType());
             if (cast == null) {
                 continue;
             }
@@ -479,7 +479,7 @@
                 valueParam = guardedParam;
             }
 
-            CodeTree implicitGuard = createImplicitGuard(builder, field, valueParam, guardedParam);
+            CodeTree implicitGuard = createTypeGuard(builder, field, valueParam, guardedParam.getTypeSystemType());
             if (implicitGuard == null) {
                 continue;
             }
@@ -492,11 +492,11 @@
         return builder.isEmpty() ? null : builder.getRoot();
     }
 
-    private CodeTree createImplicitGuard(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, ActualParameter target) {
+    private CodeTree createTypeGuard(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, TypeData targetType) {
         NodeData node = field.getNodeData();
+
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
 
-        TypeData targetType = target.getTypeSystemType();
         TypeData sourceType = source.getTypeSystemType();
 
         if (!sourceType.needsCastTo(getContext(), targetType)) {
@@ -506,14 +506,14 @@
         builder.startGroup();
 
         if (field.isShortCircuit()) {
-            ActualParameter shortCircuit = target.getPreviousParameter();
+            ActualParameter shortCircuit = source.getPreviousParameter();
             assert shortCircuit != null;
             builder.string("(");
             builder.string("!").string(valueName(shortCircuit));
             builder.string(" || ");
         }
 
-        startCallTypeSystemMethod(getContext(), builder, node, TypeSystemCodeGenerator.isTypeMethodName(target.getTypeSystemType()));
+        startCallTypeSystemMethod(getContext(), builder, node, TypeSystemCodeGenerator.isTypeMethodName(targetType));
         builder.string(valueName(source));
         builder.end().end(); // call
 
@@ -526,10 +526,9 @@
         return builder.getRoot();
     }
 
-    private CodeTree createCast(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, ActualParameter target) {
+    private CodeTree createCast(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, TypeData targetType) {
         NodeData node = field.getNodeData();
         TypeData sourceType = source.getTypeSystemType();
-        TypeData targetType = target.getTypeSystemType();
 
         if (!sourceType.needsCastTo(getContext(), targetType)) {
             return null;
@@ -537,14 +536,14 @@
 
         CodeTree condition = null;
         if (field.isShortCircuit()) {
-            ActualParameter shortCircuit = target.getPreviousParameter();
+            ActualParameter shortCircuit = source.getPreviousParameter();
             assert shortCircuit != null;
             condition = CodeTreeBuilder.singleString(valueName(shortCircuit));
         }
 
-        CodeTree value = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.asTypeMethodName(targetType), CodeTreeBuilder.singleString(valueName(target)));
-
-        return createLazyAssignment(parent, castValueName(target), target.getType(), condition, value);
+        CodeTree value = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.asTypeMethodName(targetType), CodeTreeBuilder.singleString(valueName(source)));
+
+        return createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, value);
     }
 
     /**
@@ -1416,8 +1415,7 @@
             return var;
         }
 
-        private CodeExecutableElement createGenericExecuteAndSpecialize(NodeData node) {
-
+        private CodeExecutableElement createGenericExecuteAndSpecialize(final NodeData node) {
             TypeMirror genericReturnType = node.getGenericSpecialization().getReturnType().getType();
             CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED), genericReturnType, EXECUTE_SPECIALIZE_NAME);
             method.addParameter(new CodeVariableElement(getContext().getType(int.class), "minimumState"));
@@ -1436,33 +1434,36 @@
             builder.end().end();
 
             List<SpecializationData> specializations = node.getSpecializations();
-
-            boolean firstUnreachable = true;
-            SpecializationData previous = null;
+            List<SpecializationData> filteredSpecializations = new ArrayList<>();
             for (SpecializationData current : specializations) {
-                if (current.isUninitialized()) {
+                if (current.isUninitialized() || !current.isReachable()) {
                     continue;
                 }
-                String prefix = null;
-
-                if (current.hasRewrite(getContext())) {
-                    prefix = "minimumState < " + node.getSpecializations().indexOf(current);
+                filteredSpecializations.add(current);
+            }
+
+            List<SpecializationGroup> groups = SpecializationGroup.create(filteredSpecializations);
+
+            for (SpecializationGroup group : groups) {
+                builder.tree(createExecuteTree(builder, node.getGenericSpecialization(), group, true, new CodeBlock<SpecializationData>() {
+
+                    public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
+                        return createGenericInvokeAndSpecialize(b, node.getGenericSpecialization(), current);
+                    }
+                }));
+            }
+
+            boolean firstUnreachable = true;
+            for (SpecializationData current : specializations) {
+                if (current.isUninitialized() || current.isReachable()) {
+                    continue;
                 }
-
-                if (current.isReachable()) {
-                    CodeTree execute = createGenericInvokeAndSpecialize(builder, node.getGenericSpecialization(), current);
-
-                    builder.tree(createGuardAndCast(builder, prefix, current.getNode().getGenericSpecialization(), current, true, execute, null, true, false));
-                } else {
-                    if (firstUnreachable) {
-                        if (previous != null && !previous.isGenericSpecialization(getContext()) && !previous.hasRewrite(getContext())) {
-                            emitEncounteredSynthetic(builder, current);
-                        }
-                        firstUnreachable = false;
-                    }
-                    builder.string("// unreachable ").string(current.getId()).newLine();
+                if (firstUnreachable) {
+                    emitEncounteredSynthetic(builder, current);
+                    firstUnreachable = false;
                 }
-                previous = current;
+
+                builder.string("// unreachable ").string(current.getId()).newLine();
             }
 
             return method;
@@ -1475,18 +1476,26 @@
             method.getAnnotationMirrors().add(new CodeAnnotationMirror(getContext().getTruffleTypes().getSlowPath()));
 
             addInternalValueParameters(method, node.getGenericSpecialization(), node.needsFrame(), false);
-            CodeTreeBuilder builder = method.createBuilder();
-
-            String prefix = null;
+            final CodeTreeBuilder builder = method.createBuilder();
+
             List<SpecializationData> specializations = node.getSpecializations();
-
+            List<SpecializationData> filteredSpecializations = new ArrayList<>();
             for (SpecializationData current : specializations) {
                 if (current.isUninitialized() || !current.isReachable()) {
                     continue;
                 }
-                CodeTreeBuilder execute = new CodeTreeBuilder(builder);
-                execute.tree(createGenericInvoke(builder, node.getGenericSpecialization(), current));
-                builder.tree(createGuardAndCast(builder, prefix, current.getNode().getGenericSpecialization(), current, true, execute.getRoot(), null, true, false));
+                filteredSpecializations.add(current);
+            }
+
+            List<SpecializationGroup> groups = SpecializationGroup.create(filteredSpecializations);
+
+            for (SpecializationGroup group : groups) {
+                builder.tree(createExecuteTree(builder, node.getGenericSpecialization(), group, false, new CodeBlock<SpecializationData>() {
+
+                    public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
+                        return createGenericInvoke(builder, current.getNode().getGenericSpecialization(), current);
+                    }
+                }));
             }
 
             for (SpecializationData current : specializations) {
@@ -1499,6 +1508,209 @@
             return method;
         }
 
+        private CodeTree createExecuteTree(CodeTreeBuilder outerParent, final SpecializationData source, final SpecializationGroup group, final boolean checkMinimumState,
+                        final CodeBlock<SpecializationData> guardedblock) {
+            return guard(outerParent, source, group, checkMinimumState, new CodeBlock<Void>() {
+
+                public CodeTree create(CodeTreeBuilder parent, Void value) {
+                    CodeTreeBuilder builder = parent.create();
+
+                    if (group.getSpecialization() != null) {
+                        builder.tree(guardedblock.create(builder, group.getSpecialization()));
+
+                        assert group.getChildren().isEmpty() : "missed a specialization";
+                    } else {
+                        for (SpecializationGroup childGroup : group.getChildren()) {
+                            builder.tree(createExecuteTree(builder, source, childGroup, checkMinimumState, guardedblock));
+                        }
+                    }
+
+                    return builder.getRoot();
+                }
+            });
+        }
+
+        private CodeTree guard(CodeTreeBuilder parent, SpecializationData source, SpecializationGroup group, boolean checkMinimumState, CodeBlock<Void> bodyBlock) {
+            NodeData node = source.getNode();
+
+            CodeTreeBuilder guardsBuilder = parent.create();
+            CodeTreeBuilder castBuilder = parent.create();
+            CodeTreeBuilder guardsCastBuilder = parent.create();
+
+            String guardsAnd = "";
+            String guardsCastAnd = "";
+
+            boolean minimumState = checkMinimumState;
+            if (minimumState) {
+                int groupMaxIndex = group.getMaxSpecializationIndex();
+
+                int genericIndex = node.getSpecializations().indexOf(node.getGenericSpecialization());
+                if (groupMaxIndex >= genericIndex) {
+                    // no minimum state check for an generic index
+                    minimumState = false;
+                }
+
+                if (minimumState) {
+                    // no minimum state check if alread checked by parent group
+                    int parentMaxIndex = -1;
+                    if (group.getParent() != null) {
+                        parentMaxIndex = group.getParent().getMaxSpecializationIndex();
+                    }
+                    if (groupMaxIndex == parentMaxIndex) {
+                        minimumState = false;
+                    }
+                }
+
+                if (minimumState) {
+                    guardsBuilder.string(guardsAnd);
+                    guardsBuilder.string("minimumState < " + groupMaxIndex);
+                    guardsAnd = " && ";
+                }
+            }
+
+            for (String assumption : group.getAssumptions()) {
+                guardsBuilder.string(guardsAnd);
+                guardsBuilder.string("this");
+                guardsBuilder.string(".").string(assumption).string(".isValid()");
+                guardsAnd = " && ";
+            }
+
+            int argOffset = group.getTypeGuardOffset();
+            int argIndex = argOffset;
+            for (TypeData typeData : group.getTypeGuards()) {
+
+                ActualParameter valueParam = source.getSignatureParameter(argIndex);
+
+                if (valueParam == null) {
+                    /*
+                     * If used inside a execute evaluated method then the value param may not exist.
+                     * In that case we assume that the value is executed generic or of the current
+                     * specialization.
+                     */
+                    if (group.getSpecialization() != null) {
+                        valueParam = group.getSpecialization().getSignatureParameter(argIndex);
+                    } else {
+                        valueParam = node.getGenericSpecialization().getSignatureParameter(argIndex);
+                    }
+                }
+
+                NodeChildData child = node.findChild(valueParam.getSpecification().getName());
+                if (child == null) {
+                    throw new IllegalStateException();
+                }
+
+                CodeTree implicitGuard = createTypeGuard(guardsBuilder, child, valueParam, typeData);
+                if (implicitGuard != null) {
+                    guardsBuilder.string(guardsAnd);
+                    guardsBuilder.tree(implicitGuard);
+                    guardsAnd = " && ";
+                }
+
+                CodeTree cast = createCast(castBuilder, child, valueParam, typeData);
+                if (cast != null) {
+                    castBuilder.tree(cast);
+                }
+
+                argIndex++;
+            }
+            CodeTreeBuilder builder = parent.create();
+
+            int ifCount = 0;
+            if (isElseConnectableGroup(group)) {
+                if (minimumState) {
+                    builder.startElseIf().tree(guardsBuilder.getRoot()).end().startBlock();
+                } else {
+                    builder.startElseBlock();
+                }
+                ifCount++;
+
+            } else {
+                for (GuardData guard : group.getGuards()) {
+                    if (needsTypeGuard(source, group, guard)) {
+                        guardsCastBuilder.tree(createMethodGuard(parent, guardsCastAnd, source, guard));
+                        guardsCastAnd = " && ";
+                    } else {
+                        guardsBuilder.tree(createMethodGuard(parent, guardsAnd, source, guard));
+                        guardsAnd = " && ";
+                    }
+                }
+
+                if (!guardsBuilder.isEmpty()) {
+                    builder.startIf().tree(guardsBuilder.getRoot()).end().startBlock();
+                    ifCount++;
+                }
+                builder.tree(castBuilder.getRoot());
+
+                if (!guardsCastBuilder.isEmpty()) {
+                    builder.startIf().tree(guardsCastBuilder.getRoot()).end().startBlock();
+                    ifCount++;
+                }
+            }
+
+            builder.tree(bodyBlock.create(builder, null));
+
+            builder.end(ifCount);
+            return builder.getRoot();
+        }
+
+        private boolean isElseConnectableGroup(SpecializationGroup group) {
+            if (!group.getTypeGuards().isEmpty() || !group.getAssumptions().isEmpty()) {
+                return false;
+            }
+
+            SpecializationGroup previousGroup = group.getPreviousGroup();
+            if (previousGroup != null && group.getGuards().size() == 1 && previousGroup.getGuards().size() == 1) {
+                GuardData guard = group.getGuards().get(0);
+                GuardData previousGuard = previousGroup.getGuards().get(0);
+
+                if (guard.getMethod().equals(previousGuard.getMethod())) {
+                    assert guard.isNegated() != previousGuard.isNegated();
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean needsTypeGuard(SpecializationData source, SpecializationGroup group, GuardData guard) {
+            int offset = group.getTypeGuardOffset();
+            int argIndex = 0;
+            for (ActualParameter parameter : guard.getParameters()) {
+                if (!parameter.getSpecification().isSignature()) {
+                    continue;
+                }
+                if (argIndex < offset) {
+                    // type casted in parent group
+                    continue;
+                }
+
+                int guardIndex = argIndex - offset;
+                if (guardIndex < group.getTypeGuards().size()) {
+                    TypeData requiredType = group.getTypeGuards().get(guardIndex);
+
+                    ActualParameter sourceParameter = source.findParameter(parameter.getLocalName());
+                    if (sourceParameter == null) {
+                        sourceParameter = source.getNode().getGenericSpecialization().findParameter(parameter.getLocalName());
+                    }
+
+                    if (sourceParameter.getTypeSystemType().needsCastTo(getContext(), requiredType)) {
+                        return true;
+                    }
+                }
+                argIndex++;
+            }
+            return false;
+        }
+
+        private CodeTree createMethodGuard(CodeTreeBuilder parent, String prefix, SpecializationData source, GuardData guard) {
+            CodeTreeBuilder builder = parent.create();
+            builder.string(prefix);
+            if (guard.isNegated()) {
+                builder.string("!");
+            }
+            builder.tree(createTemplateMethodCall(builder, null, source, guard, null));
+            return builder.getRoot();
+        }
+
         protected CodeTree createGenericInvoke(CodeTreeBuilder parent, SpecializationData source, SpecializationData current) {
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
 
@@ -1508,14 +1720,16 @@
                 builder.startReturn().tree(createTemplateMethodCall(builder, null, source, current, null)).end();
             }
 
-            return encloseThrowsWithFallThrough(current, builder.getRoot());
+            return encloseThrowsWithFallThrough(current, builder.getRoot(), null);
         }
 
         protected CodeTree createGenericInvokeAndSpecialize(CodeTreeBuilder parent, SpecializationData source, SpecializationData current) {
-            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
+            CodeTreeBuilder builder = parent.create();
+            CodeTreeBuilder prefix = parent.create();
 
             NodeData node = current.getNode();
 
+            String restoreNode = null;
             if (current.isGeneric() && node.isPolymorphic()) {
                 builder.startIf().string("next0 == null && minimumState > 0").end().startBlock();
                 builder.tree(createRewritePolymorphic(builder, node));
@@ -1525,10 +1739,18 @@
                 builder.end();
             } else {
                 // simple rewrite
-                builder.tree(createReturnRewriteAndInvoke(builder, null, null, source, current));
+                if (current.getExceptions().isEmpty()) {
+                    builder.tree(createGenericInvoke(builder, source, current, createReplaceCall(builder, current, null, null)));
+                } else {
+                    prefix.declaration(baseClassName(node), "restoreNode", createReplaceCall(builder, current, null, null));
+                    builder.tree(createGenericInvoke(builder, source, current, CodeTreeBuilder.singleString("restoreNode")));
+                    restoreNode = "restoreNode";
+                }
             }
-
-            return encloseThrowsWithFallThrough(current, builder.getRoot());
+            CodeTreeBuilder root = parent.create();
+            root.tree(prefix.getRoot());
+            root.tree(encloseThrowsWithFallThrough(current, builder.getRoot(), restoreNode));
+            return root.getRoot();
         }
 
         private CodeTree createRewriteGeneric(CodeTreeBuilder parent, SpecializationData source, SpecializationData current) {
@@ -1540,7 +1762,7 @@
             builder.tree(createFindRoot(builder, node, false));
             builder.end();
             builder.end();
-            builder.tree(createReturnRewriteAndInvoke(builder, "root", null, source, current));
+            builder.tree(createGenericInvoke(builder, source, current, createReplaceCall(builder, current, "root", null)));
             return builder.getRoot();
         }
 
@@ -1559,7 +1781,7 @@
             return builder.getRoot();
         }
 
-        private CodeTree encloseThrowsWithFallThrough(SpecializationData current, CodeTree tree) {
+        private CodeTree encloseThrowsWithFallThrough(SpecializationData current, CodeTree tree, String restoreNodeVarName) {
             if (current.getExceptions().isEmpty()) {
                 return tree;
             }
@@ -1569,6 +1791,16 @@
             builder.tree(tree);
             for (SpecializationThrowsData exception : current.getExceptions()) {
                 builder.end().startCatchBlock(exception.getJavaClass(), "rewriteEx");
+                if (restoreNodeVarName != null) {
+                    builder.startStatement().startCall(restoreNodeVarName, "replace").string("this");
+                    builder.startGroup();
+                    builder.startCall("createInfo0").doubleQuote("Rewrite exception thrown " + Utils.getSimpleName(exception.getJavaClass()) + ".");
+                    addInternalValueParameterNames(builder, current, current, null, false, true);
+                    builder.end();
+                    builder.end();
+                    builder.end().end();
+                }
+
                 builder.string("// fall through").newLine();
             }
             builder.end();
@@ -1576,9 +1808,28 @@
             return builder.getRoot();
         }
 
-        protected CodeTree createReturnRewriteAndInvoke(CodeTreeBuilder parent, String target, String message, SpecializationData source, SpecializationData current) {
+        protected CodeTree createGenericInvoke(CodeTreeBuilder parent, SpecializationData source, SpecializationData current, CodeTree replaceCall) {
+            CodeTreeBuilder builder = parent.create();
+            if (current.isGeneric()) {
+                builder.startReturn().tree(replaceCall).string(".").startCall(EXECUTE_GENERIC_NAME);
+                addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(), true);
+                builder.end().end();
+
+            } else if (current.getMethod() == null) {
+                builder.statement(replaceCall);
+                emitEncounteredSynthetic(builder, current);
+            } else if (!current.canBeAccessedByInstanceOf(getContext(), source.getNode().getNodeType())) {
+                builder.statement(replaceCall);
+                builder.startReturn().tree(createTemplateMethodCall(parent, null, source, current, null)).end();
+            } else {
+                replaceCall.add(new CodeTree(CodeTreeKind.STRING, null, "."));
+                builder.startReturn().tree(createTemplateMethodCall(parent, replaceCall, source, current, null)).end();
+            }
+            return builder.getRoot();
+        }
+
+        protected CodeTree createReplaceCall(CodeTreeBuilder builder, SpecializationData current, String target, String message) {
             String className = nodeSpecializationClassName(current);
-            CodeTreeBuilder builder = parent.create();
             CodeTreeBuilder replaceCall = builder.create();
             if (target != null) {
                 replaceCall.startCall(target, "replace");
@@ -1592,24 +1843,7 @@
                 replaceCall.doubleQuote(message);
             }
             replaceCall.end();
-
-            if (current.isGeneric()) {
-                replaceCall.string(".");
-                builder.startReturn().tree(replaceCall.getRoot()).startCall(EXECUTE_GENERIC_NAME);
-                addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(), true);
-                builder.end().end();
-
-            } else if (current.getMethod() == null) {
-                builder.statement(replaceCall.getRoot());
-                emitEncounteredSynthetic(builder, current);
-            } else if (!current.canBeAccessedByInstanceOf(getContext(), source.getNode().getNodeType())) {
-                builder.statement(replaceCall.getRoot());
-                builder.startReturn().tree(createTemplateMethodCall(builder, null, source, current, null)).end();
-            } else {
-                replaceCall.string(".");
-                builder.startReturn().tree(createTemplateMethodCall(builder, replaceCall.getRoot(), source, current, null)).end();
-            }
-            return builder.getRoot();
+            return replaceCall.getRoot();
         }
 
         private CodeTree createRewritePolymorphic(CodeTreeBuilder parent, NodeData node) {
@@ -2261,7 +2495,8 @@
             builder.startIf().string("depth > ").string(String.valueOf(node.getPolymorphicDepth())).end();
             builder.startBlock();
             String message = ("Polymorphic limit reached (" + node.getPolymorphicDepth() + ")");
-            builder.tree(createReturnRewriteAndInvoke(builder, "root", message, node.getGenericPolymorphicSpecialization(), node.getGenericSpecialization()));
+            builder.tree(createGenericInvoke(builder, node.getGenericPolymorphicSpecialization(), node.getGenericSpecialization(),
+                            createReplaceCall(builder, node.getGenericSpecialization(), "root", message)));
             builder.end();
 
             builder.startElseBlock();
@@ -2489,4 +2724,9 @@
 
     }
 
+    private interface CodeBlock<T> {
+
+        CodeTree create(CodeTreeBuilder parent, T value);
+
+    }
 }