001/*
002 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.truffle;
024
025import com.oracle.truffle.api.*;
026import com.oracle.truffle.api.frame.*;
027import com.oracle.truffle.api.nodes.*;
028
029public final class OptimizedOSRLoopNode extends LoopNode implements ReplaceObserver {
030
031    private int interpreterLoopCount;
032    private OptimizedCallTarget compiledTarget;
033
034    @Child private RepeatingNode repeatableNode;
035
036    private OptimizedOSRLoopNode(RepeatingNode repeatableNode) {
037        this.repeatableNode = repeatableNode;
038    }
039
040    @Override
041    public Node copy() {
042        OptimizedOSRLoopNode copy = (OptimizedOSRLoopNode) super.copy();
043        copy.compiledTarget = null;
044        copy.interpreterLoopCount = 0;
045        return copy;
046    }
047
048    @Override
049    public RepeatingNode getRepeatingNode() {
050        return repeatableNode;
051    }
052
053    @Override
054    public void executeLoop(VirtualFrame frame) {
055        if (CompilerDirectives.inInterpreter()) {
056            boolean done = false;
057            while (!done) {
058                if (compiledTarget == null) {
059                    done = profilingLoop(frame);
060                } else {
061                    done = compilingLoop(frame);
062                }
063            }
064        } else {
065            while (repeatableNode.executeRepeating(frame)) {
066                if (CompilerDirectives.inInterpreter()) {
067                    // compiled method got invalidated. We might need OSR again.
068                    executeLoop(frame);
069                    return;
070                }
071            }
072        }
073    }
074
075    private boolean profilingLoop(VirtualFrame frame) {
076        int osrThreshold = TruffleCompilerOptions.TruffleOSRCompilationThreshold.getValue();
077        int overflowLoopCount = Integer.MAX_VALUE - osrThreshold + interpreterLoopCount;
078        try {
079            while (repeatableNode.executeRepeating(frame)) {
080                try {
081                    overflowLoopCount = Math.incrementExact(overflowLoopCount);
082                } catch (ArithmeticException e) {
083                    compileLoop(frame);
084                    return false;
085                }
086            }
087        } finally {
088            reportLoopCount(overflowLoopCount - Integer.MAX_VALUE + osrThreshold - interpreterLoopCount);
089        }
090        return true;
091    }
092
093    private boolean compilingLoop(VirtualFrame frame) {
094        int iterations = 0;
095        try {
096            do {
097                OptimizedCallTarget target = compiledTarget;
098                if (target == null) {
099                    return false;
100                } else if (target.isValid()) {
101                    Object result = target.callDirect(new Object[]{frame});
102                    if (result == Boolean.TRUE) {
103                        // loop is done. No further repetitions necessary.
104                        return true;
105                    } else {
106                        invalidate(this, "OSR compilation got invalidated");
107                        return false;
108                    }
109                } else if (!target.isCompiling()) {
110                    invalidate(this, "OSR compilation failed or cancelled");
111                    return false;
112                }
113                iterations++;
114            } while (repeatableNode.executeRepeating(frame));
115        } finally {
116            reportLoopCount(iterations);
117        }
118        return true;
119    }
120
121    private void compileLoop(VirtualFrame frame) {
122        atomic(new Runnable() {
123            public void run() {
124                /*
125                 * Compilations need to run atomically as they may be scheduled by multiple threads
126                 * at the same time. This strategy lets the first thread win. Later threads will not
127                 * issue compiles.
128                 */
129                if (compiledTarget == null) {
130                    compiledTarget = compileImpl(frame);
131                    if (compiledTarget == null) {
132                        interpreterLoopCount = 0;
133                    }
134                }
135            }
136        });
137    }
138
139    private OptimizedCallTarget compileImpl(VirtualFrame frame) {
140        Node parent = getParent();
141        OptimizedCallTarget target = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(new OSRRootNode(this));
142        // to avoid a deopt on first call we provide some profiling information
143        target.profileReturnType(Boolean.TRUE);
144        target.profileReturnType(Boolean.FALSE);
145        target.profileArguments(new Object[]{frame});
146        // let the old parent re-adopt the children
147        parent.adoptChildren();
148        target.compile();
149        return target;
150    }
151
152    private void reportLoopCount(int reportIterations) {
153        if (reportIterations != 0) {
154            interpreterLoopCount += reportIterations;
155            getRootNode().reportLoopCount(reportIterations);
156        }
157    }
158
159    public boolean nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
160        invalidate(newNode, reason);
161        return false;
162    }
163
164    private void invalidate(Object source, CharSequence reason) {
165        OptimizedCallTarget target = this.compiledTarget;
166        if (target != null) {
167            target.invalidate(source, reason);
168            compiledTarget = null;
169            interpreterLoopCount = 0;
170        }
171    }
172
173    public static LoopNode create(RepeatingNode repeat) {
174        if (TruffleCompilerOptions.TruffleOSR.getValue()) {
175            return new OptimizedOSRLoopNode(repeat);
176        } else {
177            return new OptimizedLoopNode(repeat);
178        }
179    }
180
181    private static class OSRRootNode extends RootNode {
182
183        @Child private OptimizedOSRLoopNode loopNode;
184
185        public OSRRootNode(OptimizedOSRLoopNode loop) {
186            super(TruffleLanguage.class, loop.getSourceSection(), loop.getRootNode().getFrameDescriptor());
187            this.loopNode = loop;
188        }
189
190        @Override
191        public Object execute(VirtualFrame frame) {
192            VirtualFrame parentFrame = (VirtualFrame) frame.getArguments()[0];
193            while (loopNode.getRepeatingNode().executeRepeating(parentFrame)) {
194                if (CompilerDirectives.inInterpreter()) {
195                    return Boolean.FALSE;
196                }
197            }
198            return Boolean.TRUE;
199        }
200
201        @Override
202        public boolean isCloningAllowed() {
203            return false;
204        }
205
206        @Override
207        public String toString() {
208            return loopNode.getRepeatingNode().toString() + "<OSR>";
209        }
210
211    }
212
213}