changeset 21930:b09503284ac8

Limit inlining depth during partial evaluation to avoid StackOverflowError, provide useful error message instead
author Christian Wimmer <christian.wimmer@oracle.com>
date Thu, 11 Jun 2015 16:20:13 -0700
parents ae5bee2d61b2
children 40aff2bb1880
files graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/SimplePartialEvaluationTest.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/nodes/RecursionTestNode.java
diffstat 3 files changed, 95 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Thu Jun 11 15:14:14 2015 -0700
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Thu Jun 11 16:20:13 2015 -0700
@@ -44,6 +44,7 @@
 import com.oracle.jvmci.code.*;
 import com.oracle.jvmci.debug.*;
 import com.oracle.jvmci.meta.*;
+import com.oracle.jvmci.options.*;
 
 /**
  * A graph decoder that performs partial evaluation, i.e., that performs method inlining and
@@ -59,6 +60,11 @@
  */
 public abstract class PEGraphDecoder extends SimplifyingGraphDecoder {
 
+    public static class Options {
+        @Option(help = "Maximum inlining depth during partial evaluation before reporting an infinite recursion")//
+        public static final OptionValue<Integer> InliningDepthError = new OptionValue<>(200);
+    }
+
     protected class PEMethodScope extends MethodScope {
         /** The state of the caller method. Only non-null during method inlining. */
         protected final PEMethodScope caller;
@@ -439,6 +445,10 @@
             return false;
         }
 
+        if (methodScope.inliningDepth > Options.InliningDepthError.getValue()) {
+            throw tooDeepInlining(methodScope);
+        }
+
         for (InlineInvokePlugin plugin : methodScope.inlineInvokePlugins) {
             plugin.notifyBeforeInline(inlineMethod);
         }
@@ -523,6 +533,23 @@
         return true;
     }
 
+    private static RuntimeException tooDeepInlining(PEMethodScope methodScope) {
+        HashMap<ResolvedJavaMethod, Integer> methodCounts = new HashMap<>();
+        for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) {
+            Integer oldCount = methodCounts.get(cur.method);
+            methodCounts.put(cur.method, oldCount == null ? 1 : oldCount + 1);
+        }
+
+        List<Map.Entry<ResolvedJavaMethod, Integer>> methods = new ArrayList<>(methodCounts.entrySet());
+        methods.sort((e1, e2) -> -Integer.compare(e1.getValue(), e2.getValue()));
+
+        StringBuilder msg = new StringBuilder("Too deep inlining, probably caused by recursive inlining. Inlined methods ordered by inlining frequency:");
+        for (Map.Entry<ResolvedJavaMethod, Integer> entry : methods) {
+            msg.append(System.lineSeparator()).append(entry.getKey().format("%H.%n(%p) [")).append(entry.getValue()).append("]");
+        }
+        throw new BailoutException(msg.toString());
+    }
+
     public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, AbstractBeginNode lastBlock) {
         assert lastBlock.isAlive();
         FixedNode n;
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/SimplePartialEvaluationTest.java	Thu Jun 11 15:14:14 2015 -0700
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/SimplePartialEvaluationTest.java	Thu Jun 11 16:20:13 2015 -0700
@@ -22,9 +22,11 @@
  */
 package com.oracle.graal.truffle.test;
 
-import com.oracle.jvmci.code.SourceStackTrace;
+import com.oracle.jvmci.code.*;
+
 import org.junit.*;
 
+import com.oracle.graal.replacements.*;
 import com.oracle.graal.truffle.test.nodes.*;
 import com.oracle.truffle.api.frame.*;
 
@@ -150,4 +152,20 @@
         AbstractTestNode result = new LambdaTestNode();
         assertPartialEvalEquals("constant42", new RootTestNode(fd, "constantValue", result));
     }
+
+    @Test
+    public void allowedRecursion() {
+        /* Recursion depth just below the threshold that reports it as too deep recursion. */
+        FrameDescriptor fd = new FrameDescriptor();
+        AbstractTestNode result = new RecursionTestNode(PEGraphDecoder.Options.InliningDepthError.getValue() - 5);
+        assertPartialEvalEquals("constant42", new RootTestNode(fd, "allowedRecursion", result));
+    }
+
+    @Test(expected = BailoutException.class)
+    public void tooDeepRecursion() {
+        /* Recursion depth just above the threshold that reports it as too deep recursion. */
+        FrameDescriptor fd = new FrameDescriptor();
+        AbstractTestNode result = new RecursionTestNode(PEGraphDecoder.Options.InliningDepthError.getValue());
+        assertPartialEvalEquals("constant42", new RootTestNode(fd, "tooDeepRecursion", result));
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/nodes/RecursionTestNode.java	Thu Jun 11 16:20:13 2015 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2015, 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.graal.truffle.test.nodes;
+
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+@NodeInfo
+public class RecursionTestNode extends AbstractTestNode {
+
+    private final int depth;
+
+    public RecursionTestNode(int depth) {
+        this.depth = depth;
+    }
+
+    @Override
+    public int execute(VirtualFrame frame) {
+        return a(depth);
+    }
+
+    private static int a(int depth) {
+        if (depth == 0) {
+            return 42;
+        } else {
+            return a(depth - 1);
+        }
+    }
+}