view 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 source

/*
 * 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;
        }

    }

}