# HG changeset patch # User Christian Humer # Date 1392855709 -3600 # Node ID f46cab39a9a25a7db52e6420570b82470d2b1f79 # Parent 39076a984c33ef0f7b1ddf6e70bf3d7d360d0616 Truffle: Updated inlining API. Pushed inlining implementation to the Truffle runtime. diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java Thu Feb 20 01:21:49 2014 +0100 @@ -24,10 +24,12 @@ */ package com.oracle.truffle.api; +import com.oracle.truffle.api.nodes.*; + /** * An observer that is notified whenever a child node is replaced. */ public interface ReplaceObserver { - void nodeReplaced(); + void nodeReplaced(Node oldNode, Node newNode, String reason); } diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java Thu Feb 20 01:21:49 2014 +0100 @@ -50,6 +50,8 @@ */ RootCallTarget createCallTarget(RootNode rootNode); + CallNode createCallNode(CallTarget target); + /** * Creates a new assumption object that can be checked and invalidated. * diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java Thu Feb 20 01:21:49 2014 +0100 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.impl; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +public class DefaultCallNode extends CallNode { + + public DefaultCallNode(CallTarget target) { + super(target); + } + + @Override + public Object call(PackedFrame caller, Arguments arguments) { + return getCallTarget().call(caller, arguments); + } + + @Override + public void inline() { + } + + @Override + public CallTarget getSplitCallTarget() { + return null; + } + + @Override + public boolean split() { + return false; + } + + @Override + public boolean isSplittable() { + return false; + } + + @Override + public boolean isInlinable() { + return false; + } + + @Override + public RootNode getInlinedRoot() { + return null; + } + + @Override + public boolean isInlined() { + return false; + } + + @Override + public String toString() { + return getParent() != null ? getParent().toString() : super.toString(); + } +} diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java Thu Feb 20 01:21:49 2014 +0100 @@ -53,6 +53,10 @@ return new DefaultCallTarget(rootNode); } + public CallNode createCallNode(CallTarget target) { + return new DefaultCallNode(target); + } + @Override public VirtualFrame createVirtualFrame(PackedFrame caller, Arguments arguments, FrameDescriptor frameDescriptor) { return new DefaultVirtualFrame(frameDescriptor, caller, arguments); diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java Thu Feb 20 01:21:49 2014 +0100 @@ -26,12 +26,13 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.impl.*; /** * This node represents a call to a static {@link CallTarget}. This node should be used whenever a * {@link CallTarget} is considered constant at a certain location in the tree. This enables the - * Truffle runtime to perform inlining or other optimizations for this call-site. + * Truffle runtime to perform inlining or other optimizations for this call-site. This class is + * intended to be implemented by truffle runtime implementors and not by guest languague + * implementors. * * @see #create(CallTarget) to create a CallNode instance. */ @@ -39,7 +40,7 @@ protected final CallTarget callTarget; - private CallNode(CallTarget callTarget) { + protected CallNode(CallTarget callTarget) { this.callTarget = callTarget; } @@ -59,12 +60,6 @@ */ public abstract Object call(PackedFrame caller, Arguments arguments); - /** - * Returns true if the {@link CallTarget} contained in this {@link CallNode} can be - * inlined. A {@link CallTarget} is considered inlinable if it was created using - * {@link TruffleRuntime#createCallTarget(RootNode)} and if the enclosed {@link RootNode} - * returns true for {@link RootNode#isInlinable()}. - */ public abstract boolean isInlinable(); /** @@ -72,209 +67,30 @@ */ public abstract boolean isInlined(); - /** - * Enforces an inlining optimization on this {@link CallNode} instance. If not performed - * manually the Truffle runtime may perform inlining using an heuristic to optimize the - * performance of the execution. It is recommended to implement an version of - * {@link RootNode#inline()} that adapts the inlining for possible guest language specific - * behavior. If the this {@link CallNode} is not inlinable or is already inlined - * false is returned. - * - * @return true if the inlining operation was successful. - */ - public abstract boolean inline(); + public abstract void inline(); + + public abstract boolean isSplittable(); - /** - * Returns the inlined root node if the call node was inlined. If the {@link CallNode} was not - * inlined null is returned. - * - * @return the inlined root node returned by {@link RootNode#inline()} - */ - public RootNode getInlinedRoot() { - return null; - } + public abstract boolean split(); + + public abstract CallTarget getSplitCallTarget(); + + public abstract RootNode getInlinedRoot(); /** * Creates a new {@link CallNode} using a {@link CallTarget}. * * @param target the {@link CallTarget} to call * @return a call node that calls the provided target - */ - public static CallNode create(CallTarget target) { - if (isInlinable(target)) { - return new InlinableCallNode((RootCallTarget) target); - } else { - return new DefaultCallNode(target); - } - } - - /** - * Warning: this is internal API and may change without notice. - */ - public interface CompilerCallView { - - int getCallCount(); - - void resetCallCount(); - - void store(Object value); - - Object load(); - } - - /** - * Warning: this is internal API and may change without notice. + * @deprecated use {@link TruffleRuntime#createCallNode(CallTarget)} instead */ - public CompilerCallView getCompilerCallView() { - return null; - } - - private static boolean isInlinable(CallTarget callTarget) { - if (callTarget instanceof RootCallTarget) { - return (((RootCallTarget) callTarget).getRootNode()).isInlinable(); - } - return false; - } - - @Override - public String toString() { - return getParent() != null ? getParent().toString() : super.toString(); - } - - static final class DefaultCallNode extends CallNode { - - public DefaultCallNode(CallTarget target) { - super(target); - } - - @Override - public Object call(PackedFrame caller, Arguments arguments) { - return callTarget.call(caller, arguments); - } - - @Override - public boolean inline() { - return false; - } - - @Override - public boolean isInlinable() { - return false; - } - - @Override - public boolean isInlined() { - return false; - } - + @Deprecated + public static CallNode create(CallTarget target) { + return Truffle.getRuntime().createCallNode(target); } - static final class InlinableCallNode extends CallNode implements CompilerCallView { - - private int callCount; - - public InlinableCallNode(RootCallTarget target) { - super(target); - } - - @Override - public Object call(PackedFrame parentFrame, Arguments arguments) { - if (CompilerDirectives.inInterpreter()) { - callCount++; - } - return callTarget.call(parentFrame, arguments); - } - - @Override - public boolean inline() { - DefaultCallTarget defaultTarget = (DefaultCallTarget) getCallTarget(); - RootNode originalRootNode = defaultTarget.getRootNode(); - if (originalRootNode.isInlinable()) { - RootNode inlinedRootNode = defaultTarget.getRootNode().inline(); - inlinedRootNode.setCallTarget(callTarget); - inlinedRootNode.setParentInlinedCall(this); - replace(new InlinedCallNode(defaultTarget, inlinedRootNode)); - return true; - } - return false; - } - - @Override - public boolean isInlined() { - return false; - } - - @Override - public boolean isInlinable() { - return true; - } - - @Override - public CompilerCallView getCompilerCallView() { - return this; - } - - /* Truffle internal API. */ - public int getCallCount() { - return callCount; - } - - /* Truffle internal API. */ - public void resetCallCount() { - callCount = 0; - } - - private Object storedCompilerInfo; - - public void store(Object value) { - this.storedCompilerInfo = value; - } - - public Object load() { - return storedCompilerInfo; - } - - } - - static final class InlinedCallNode extends CallNode { - - private final RootNode inlinedRoot; - - public InlinedCallNode(DefaultCallTarget callTarget, RootNode inlinedRoot) { - super(callTarget); - this.inlinedRoot = inlinedRoot; - } - - @Override - public Object call(PackedFrame caller, Arguments arguments) { - return inlinedRoot.execute(Truffle.getRuntime().createVirtualFrame(caller, arguments, inlinedRoot.getFrameDescriptor())); - } - - @Override - public InlinedCallNode copy() { - return new InlinedCallNode((DefaultCallTarget) getCallTarget(), NodeUtil.cloneNode(inlinedRoot)); - } - - @Override - public RootNode getInlinedRoot() { - return inlinedRoot; - } - - @Override - public boolean inline() { - return false; - } - - @Override - public boolean isInlinable() { - return true; - } - - @Override - public boolean isInlined() { - return true; - } - + protected final void installParentInlinedCall() { + getInlinedRoot().addParentInlinedCall(this); } } diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Thu Feb 20 01:21:49 2014 +0100 @@ -185,7 +185,7 @@ if (!NodeUtil.replaceChild(this.parent, this, newNode)) { fixupTree(); } - reportReplace(); + reportReplace(this, newNode, reason); return newNode; } @@ -245,11 +245,11 @@ return false; } - private void reportReplace() { - RootNode rootNode = NodeUtil.findOutermostRootNode(this); - if (rootNode != null) { - if (rootNode.getCallTarget() instanceof ReplaceObserver) { - ((ReplaceObserver) rootNode.getCallTarget()).nodeReplaced(); + private void reportReplace(Node oldNode, Node newNode, String reason) { + Collection targets = NodeUtil.findOutermostCallTargets(this); + for (CallTarget target : targets) { + if (target instanceof ReplaceObserver) { + ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason); } } } @@ -395,7 +395,7 @@ * * @return the {@link RootNode} or {@code null} if there is none. */ - protected final RootNode getRootNode() { + public final RootNode getRootNode() { Node rootNode = this; while (rootNode.getParent() != null) { assert !(rootNode instanceof RootNode) : "root node must not have a parent"; diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Thu Feb 20 01:21:49 2014 +0100 @@ -451,13 +451,28 @@ return null; } + public static List findOutermostCallTargets(Node node) { + RootNode root = node.getRootNode(); + if (root == null) { + return Collections.emptyList(); + } + List roots = new ArrayList<>(); + roots.add(root.getCallTarget()); + for (CallNode callNode : root.getParentInlinedCalls()) { + roots.addAll(findOutermostCallTargets(callNode)); + } + return roots; + } + /** * Returns the outermost not inlined {@link RootNode} which is a parent of this node. * - * @see RootNode#getParentInlinedCall() + * @see RootNode#getParentInlinedCalls() * @param node to search * @return the outermost {@link RootNode} + * @deprecated use {@link #findOutermostCallTargets(Node)} */ + @Deprecated public static RootNode findOutermostRootNode(Node node) { Node parent = node; while (parent != null) { diff -r 39076a984c33 -r f46cab39a9a2 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Wed Feb 19 00:39:44 2014 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Thu Feb 20 01:21:49 2014 +0100 @@ -24,6 +24,8 @@ */ package com.oracle.truffle.api.nodes; +import java.util.*; + import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.*; import com.oracle.truffle.api.frame.*; @@ -42,7 +44,7 @@ * Internal field to keep reference to the inlined call node. The inlined parent should not be * the same as the Node parent to keep the same tree hierarchy if inlined vs not inlined. */ - @CompilationFinal private CallNode parentInlinedCall; + @CompilationFinal private List parentInlinedCalls = new ArrayList<>(); protected RootNode() { this(null, null); @@ -62,55 +64,38 @@ } /** - * Creates a copy of the current {@link RootNode} for use as inlined AST. The default - * implementation copies this {@link RootNode} and all its children recursively. It is - * recommended to override this method to provide an implementation that copies an uninitialized - * version of this AST. An uninitialized version of an AST was usually never executed which - * means that it has not yet collected any profiling feedback. Please note that changes in the - * behavior of this method might also require changes in {@link #getInlineNodeCount()}. - * - * @see RootNode#getInlineNodeCount() - * @see RootNode#isInlinable() - * - * @return the copied RootNode for inlining - * @throws UnsupportedOperationException if {@link #isInlinable()} returns false + * @deprecated Not required anymore. Do not use. */ + @Deprecated public RootNode inline() { if (!isInlinable()) { throw new UnsupportedOperationException("Inlining is not enabled."); } - return NodeUtil.cloneNode(this); + return split(); + } + + /** + * @deprecated Not required anymore. Do not use. + */ + @Deprecated + public int getInlineNodeCount() { + return 0; } /** - * Returns the number of nodes that would be returned if {@link #inline()} would get invoked. - * This node count may be used for the calculation in a smart inlining heuristic. - * - * @see RootNode#inline() - * @see RootNode#isInlinable() - * - * @return the number of nodes that will get inlined - * @throws UnsupportedOperationException if {@link #isInlinable()} returns false + * @deprecated Not required anymore. Do not use. */ - public int getInlineNodeCount() { - if (!isInlinable()) { - throw new UnsupportedOperationException("Inlining is not enabled."); - } - return NodeUtil.countNodes(this); + @Deprecated + public boolean isInlinable() { + return true; } - /** - * Returns true if this RootNode can be inlined. If this method returns true implementations of - * {@link #inline()} and {@link #getInlineNodeCount()} must be provided. Returns - * true by default. - * - * @see RootNode#inline() - * @see RootNode#getInlineNodeCount() - * - * @return true if this RootNode can be inlined - */ - public boolean isInlinable() { - return true; + public RootNode split() { + return NodeUtil.cloneNode(this); + } + + public boolean isSplittable() { + return false; } /** @@ -118,8 +103,11 @@ * heuristics can use the loop count to guide compilation and inlining. */ public void reportLoopCount(int count) { - if (getCallTarget() instanceof LoopCountReceiver) { - ((LoopCountReceiver) getCallTarget()).reportLoopCount(count); + List callTargets = NodeUtil.findOutermostCallTargets(this); + for (CallTarget target : callTargets) { + if (target instanceof LoopCountReceiver) { + ((LoopCountReceiver) target).reportLoopCount(count); + } } } @@ -144,18 +132,19 @@ } /* Internal API. Do not use. */ - void setParentInlinedCall(CallNode inlinedParent) { - this.parentInlinedCall = inlinedParent; + void addParentInlinedCall(CallNode inlinedParent) { + this.parentInlinedCalls.add(inlinedParent); + } + + public final List getParentInlinedCalls() { + return Collections.unmodifiableList(parentInlinedCalls); } /** - * Returns the {@link CallNode} that uses this {@link RootNode} for an inlined call. Returns - * null if this {@link RootNode} is not inlined into a caller. This method can be - * used to also traverse parent {@link CallTarget} that have been inlined into this call. - * - * @return the responsible {@link CallNode} for inlining. + * @deprecated use {@link #getParentInlinedCalls()} instead. */ + @Deprecated public final CallNode getParentInlinedCall() { - return parentInlinedCall; + return parentInlinedCalls.isEmpty() ? null : parentInlinedCalls.get(0); } }