diff graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java @ 13803:e076c87ab175

Truffle: refactored inlining interfaces to a more compact CallNode.
author Christian Humer <christian.humer@gmail.com>
date Fri, 24 Jan 2014 15:55:41 +0100
parents
children 3840d61e0e68
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Fri Jan 24 15:55:41 2014 +0100
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2012, 2013, 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.nodes;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
+
+/**
+ * This node represents a call to a constant {@link CallTarget} in the Truffle AST. This node should
+ * be used whenever a {@link CallTarget} is considered constant at a certain location. This enables
+ * the Truffle runtime to perform inlining or other optimizations for this call-site.
+ * 
+ * @see #create(CallTarget) to create a CallNode instance.
+ */
+public abstract class CallNode extends Node {
+
+    protected final CallTarget callTarget;
+
+    private CallNode(CallTarget callTarget) {
+        this.callTarget = callTarget;
+    }
+
+    /**
+     * Returns the constant {@link CallTarget} that is associated with this {@link CallNode}.
+     */
+    public CallTarget getCallTarget() {
+        return callTarget;
+    }
+
+    /**
+     * Calls this constant target passing a caller frame and arguments.
+     * 
+     * @param caller the caller frame
+     * @param arguments the arguments that should be passed to the callee
+     * @return the return result of the call
+     */
+    public abstract Object call(PackedFrame caller, Arguments arguments);
+
+    /**
+     * Returns <code>true</code> if 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 <code>true</code> for {@link RootNode#isInlinable()}.
+     */
+    public boolean isInlinable() {
+        return false;
+    }
+
+    /**
+     * Returns true if this {@link CallNode} was already inlined.
+     */
+    public boolean isInlined() {
+        return false;
+    }
+
+    /**
+     * 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 optimized version of
+     * {@link RootNode#inline()}.
+     * 
+     * @return true if the inlining operation was successful.
+     * @throws IllegalStateException if {@link #isInlinable()} is false.
+     */
+    public boolean inline() {
+        CompilerDirectives.transferToInterpreter();
+        if (!isInlinable()) {
+            throw new IllegalStateException("Invoked inline on a non inlinable CallNode.");
+        }
+        assert !isInlined();
+        return false;
+    }
+
+    /**
+     * Creates a new {@link CallNode} using a constant {@link CallTarget}.
+     * 
+     * @param target the {@link CallTarget} the {@link CallNode} should call
+     * @return a call node that calls the provided target
+     */
+    public static CallNode create(CallTarget target) {
+        if (isInlinable(target)) {
+            return new InlinableCallNode(target);
+        } else {
+            return new DefaultCallNode(target);
+        }
+    }
+
+    /**
+     * Warning: this is internal API and may change without notice.
+     */
+    public static int internalGetCallCount(CallNode callNode) {
+        if (callNode.isInlinable()) {
+            return ((InlinableCallNode) callNode).getCallCount();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Warning: this is internal API and may change without notice.
+     */
+    public static void internalResetCallCount(CallNode callNode) {
+        if (callNode.isInlinable()) {
+            ((InlinableCallNode) callNode).resetCallCount();
+            return;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    private static boolean isInlinable(CallTarget callTarget) {
+        if (callTarget instanceof DefaultCallTarget) {
+            return (((DefaultCallTarget) callTarget).getRootNode()).isInlinable();
+        }
+        return false;
+    }
+
+    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);
+        }
+
+    }
+
+    static final class InlinableCallNode extends CallNode {
+
+        private int callCount;
+
+        public InlinableCallNode(CallTarget target) {
+            super(target);
+        }
+
+        @Override
+        public Object call(PackedFrame parentFrame, Arguments arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+            }
+            return callTarget.call(parentFrame, arguments);
+        }
+
+        @Override
+        public boolean inline() {
+            super.inline();
+            DefaultCallTarget defaultTarget = (DefaultCallTarget) getCallTarget();
+            RootNode originalRootNode = defaultTarget.getRootNode();
+            boolean inlined = false;
+            if (originalRootNode.isInlinable()) {
+                RootNode inlinedRootNode = defaultTarget.getRootNode().inline();
+                replace(new InlinedCallNode(defaultTarget, inlinedRootNode));
+                inlined = true;
+            }
+            return inlined;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return true;
+        }
+
+        /* Truffle internal API. */
+        int getCallCount() {
+            return callCount;
+        }
+
+        /* Truffle internal API. */
+        void resetCallCount() {
+            callCount = 0;
+        }
+
+    }
+
+    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 boolean isInlinable() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return true;
+        }
+
+    }
+
+}