changeset 20097:e8e55ebcf069

Truffle: implement language agnostic OSR support.
author Christian Humer <christian.humer@gmail.com>
date Tue, 31 Mar 2015 02:16:33 +0200
parents 3844fb65016c
children 5d069d341b7d
files graal/com.oracle.graal.truffle.test/sl/TestOSR.sl graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java
diffstat 4 files changed, 233 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle.test/sl/TestOSR.sl	Tue Mar 31 02:16:33 2015 +0200
@@ -0,0 +1,13 @@
+function test() {
+  i = 0;
+  sum = 0;  
+  while (i < 300000) { 
+    sum = sum +  i;
+    i = i + 1;  
+  }
+  return sum;
+}
+
+function main() {  
+  test();
+}  
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java	Mon Mar 30 22:52:03 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultLoopNodeFactory.java	Tue Mar 31 02:16:33 2015 +0200
@@ -29,7 +29,7 @@
 public class DefaultLoopNodeFactory implements LoopNodeFactory {
 
     public LoopNode create(RepeatingNode repeatingNode) {
-        return new OptimizedLoopNode(repeatingNode);
+        return OptimizedOSRLoopNode.create(repeatingNode);
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedOSRLoopNode.java	Tue Mar 31 02:16:33 2015 +0200
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 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;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+public final class OptimizedOSRLoopNode extends LoopNode implements ReplaceObserver {
+
+    private static final int OSR_THRESHOLD = TruffleCompilerOptions.TruffleOSRCompilationThreshold.getValue();
+
+    private int interpreterLoopCount;
+    private OptimizedCallTarget compiledTarget;
+
+    @Child private RepeatingNode repeatableNode;
+
+    private OptimizedOSRLoopNode(RepeatingNode repeatableNode) {
+        this.repeatableNode = repeatableNode;
+    }
+
+    @Override
+    public Node copy() {
+        OptimizedOSRLoopNode copy = (OptimizedOSRLoopNode) super.copy();
+        copy.compiledTarget = null;
+        copy.interpreterLoopCount = 0;
+        return copy;
+    }
+
+    @Override
+    public RepeatingNode getRepeatingNode() {
+        return repeatableNode;
+    }
+
+    @Override
+    public void executeLoop(VirtualFrame frame) {
+        if (CompilerDirectives.inInterpreter()) {
+            boolean done = false;
+            while (!done) {
+                if (compiledTarget == null) {
+                    done = profilingLoop(frame);
+                } else {
+                    done = compilingLoop(frame);
+                }
+            }
+        } else {
+            while (repeatableNode.executeRepeating(frame)) {
+                if (CompilerDirectives.inInterpreter()) {
+                    // compiled method got invalidated. We might need OSR again.
+                    executeLoop(frame);
+                    return;
+                }
+            }
+        }
+    }
+
+    private boolean profilingLoop(VirtualFrame frame) {
+        int overflowLoopCount = Integer.MAX_VALUE - OSR_THRESHOLD + interpreterLoopCount;
+        try {
+            while (repeatableNode.executeRepeating(frame)) {
+                try {
+                    overflowLoopCount = Math.incrementExact(overflowLoopCount);
+                } catch (ArithmeticException e) {
+                    compileLoop(frame);
+                    return false;
+                }
+            }
+        } finally {
+            reportLoopCount(overflowLoopCount - Integer.MAX_VALUE + OSR_THRESHOLD - interpreterLoopCount);
+        }
+        return true;
+    }
+
+    private boolean compilingLoop(VirtualFrame frame) {
+        int iterations = 0;
+        try {
+            do {
+                OptimizedCallTarget target = compiledTarget;
+                if (target == null) {
+                    return false;
+                } else if (target.isValid()) {
+                    Object result = target.callDirect(new Object[]{frame});
+                    if (result == Boolean.TRUE) {
+                        // loop is done. No further repetitions necessary.
+                        return true;
+                    } else {
+                        invalidate(this, "OSR compilation got invalidated");
+                        return false;
+                    }
+                } else if (!target.isCompiling()) {
+                    invalidate(this, "OSR compilation failed or cancelled");
+                    return false;
+                }
+                iterations++;
+            } while (repeatableNode.executeRepeating(frame));
+        } finally {
+            reportLoopCount(iterations);
+        }
+        return true;
+    }
+
+    private void compileLoop(VirtualFrame frame) {
+        atomic(new Runnable() {
+            public void run() {
+                /*
+                 * Compilations need to run atomically as they may be scheduled by multiple threads
+                 * at the same time. This strategy lets the first thread win. Later threads will not
+                 * issue compiles.
+                 */
+                if (compiledTarget == null) {
+                    compiledTarget = compileImpl(frame);
+                    if (compiledTarget == null) {
+                        interpreterLoopCount = 0;
+                    }
+                }
+            }
+        });
+    }
+
+    private OptimizedCallTarget compileImpl(VirtualFrame frame) {
+        Node parent = getParent();
+        OptimizedCallTarget target = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(new OSRRootNode(this));
+        // to avoid a deopt on first call we provide some profiling information
+        target.profileReturnType(Boolean.TRUE);
+        target.profileReturnType(Boolean.FALSE);
+        target.profileArguments(new Object[]{frame});
+        // let the old parent re-adopt the children
+        parent.adoptChildren();
+        target.compile();
+        return target;
+    }
+
+    private void reportLoopCount(int reportIterations) {
+        if (reportIterations != 0) {
+            interpreterLoopCount += reportIterations;
+            getRootNode().reportLoopCount(reportIterations);
+        }
+    }
+
+    public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
+        invalidate(newNode, reason);
+    }
+
+    private void invalidate(Object source, CharSequence reason) {
+        OptimizedCallTarget target = this.compiledTarget;
+        if (target != null) {
+            target.invalidate(source, reason);
+            compiledTarget = null;
+            interpreterLoopCount = 0;
+        }
+    }
+
+    public static LoopNode create(RepeatingNode repeat) {
+        if (TruffleCompilerOptions.TruffleOSR.getValue()) {
+            return new OptimizedOSRLoopNode(repeat);
+        } else {
+            return new OptimizedLoopNode(repeat);
+        }
+    }
+
+    private static class OSRRootNode extends RootNode {
+
+        @Child private OptimizedOSRLoopNode loopNode;
+
+        public OSRRootNode(OptimizedOSRLoopNode loop) {
+            super(loop.getSourceSection(), loop.getRootNode().getFrameDescriptor());
+            this.loopNode = loop;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            VirtualFrame parentFrame = (VirtualFrame) frame.getArguments()[0];
+            while (loopNode.getRepeatingNode().executeRepeating(parentFrame)) {
+                if (CompilerDirectives.inInterpreter()) {
+                    return Boolean.FALSE;
+                }
+            }
+            return Boolean.TRUE;
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return loopNode.getRepeatingNode().toString() + "<OSR>";
+        }
+
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Mon Mar 30 22:52:03 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Tue Mar 31 02:16:33 2015 +0200
@@ -98,6 +98,12 @@
     @Option(help = "Experimental. New splitting only: Split everything aggressively. ", type = OptionType.Debug)
     public static final OptionValue<Boolean> TruffleSplittingAggressive = new OptionValue<>(false);
 
+    @Option(help = "Enable on stack replacement for Truffle loops.", type = OptionType.Debug)
+    public static final OptionValue<Boolean> TruffleOSR = new OptionValue<>(true);
+
+    @Option(help = "Number of loop iterations until on-stack-replacement compilation is triggered.", type = OptionType.Debug)
+    public static final OptionValue<Integer> TruffleOSRCompilationThreshold = new OptionValue<>(10000);
+
     @Option(help = "Disable call target splitting if tree size exceeds this limit", type = OptionType.Debug)
     public static final OptionValue<Integer> TruffleSplittingMaxCalleeSize = new OptionValue<>(100);