001/*
002 * Copyright (c) 2013, 2014, 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.CompilerDirectives.CompilationFinal;
027import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
028import com.oracle.truffle.api.frame.*;
029import com.oracle.truffle.api.nodes.*;
030
031/**
032 * A call node with a constant {@link CallTarget} that can be optimized by Graal.
033 */
034@NodeInfo
035public final class OptimizedDirectCallNode extends DirectCallNode implements MaterializedFrameNotify {
036
037    private int callCount;
038    private boolean inliningForced;
039
040    @CompilationFinal private OptimizedCallTarget splitCallTarget;
041    @CompilationFinal private FrameAccess outsideFrameAccess = FrameAccess.NONE;
042
043    private final TruffleSplittingStrategy splittingStrategy;
044    private final GraalTruffleRuntime runtime;
045
046    public OptimizedDirectCallNode(GraalTruffleRuntime runtime, OptimizedCallTarget target) {
047        super(target);
048        this.runtime = runtime;
049        if (TruffleCompilerOptions.TruffleSplittingNew.getValue()) {
050            this.splittingStrategy = new DefaultTruffleSplittingStrategyNew(this);
051        } else {
052            this.splittingStrategy = new DefaultTruffleSplittingStrategy(this);
053        }
054    }
055
056    @Override
057    public Object call(VirtualFrame frame, Object[] arguments) {
058        if (CompilerDirectives.inInterpreter()) {
059            onInterpreterCall(arguments);
060        }
061        Object result = callProxy(this, getCurrentCallTarget(), frame, arguments, true);
062
063        if (CompilerDirectives.inInterpreter()) {
064            afterInterpreterCall(result);
065        }
066        return result;
067    }
068
069    private void afterInterpreterCall(Object result) {
070        splittingStrategy.afterCall(result);
071    }
072
073    public static Object callProxy(MaterializedFrameNotify notify, CallTarget callTarget, VirtualFrame frame, Object[] arguments, boolean direct) {
074        try {
075            if (notify.getOutsideFrameAccess() != FrameAccess.NONE) {
076                CompilerDirectives.materialize(frame);
077            }
078            if (direct) {
079                return ((OptimizedCallTarget) callTarget).callDirect(arguments);
080            } else {
081                return callTarget.call(arguments);
082            }
083        } finally {
084            // this assertion is needed to keep the values from being cleared as non-live locals
085            assert notify != null & callTarget != null & frame != null;
086        }
087    }
088
089    @Override
090    public boolean isInlinable() {
091        return true;
092    }
093
094    @Override
095    public void forceInlining() {
096        inliningForced = true;
097    }
098
099    @Override
100    public boolean isInliningForced() {
101        return inliningForced;
102    }
103
104    @Override
105    public FrameAccess getOutsideFrameAccess() {
106        return outsideFrameAccess;
107    }
108
109    @Override
110    public void setOutsideFrameAccess(FrameAccess outsideFrameAccess) {
111        this.outsideFrameAccess = outsideFrameAccess;
112    }
113
114    @Override
115    public boolean isCallTargetCloningAllowed() {
116        return getCallTarget().getRootNode().isCloningAllowed();
117    }
118
119    @Override
120    public OptimizedCallTarget getCallTarget() {
121        return (OptimizedCallTarget) super.getCallTarget();
122    }
123
124    public int getCallCount() {
125        return callCount;
126    }
127
128    @Override
129    public OptimizedCallTarget getCurrentCallTarget() {
130        return (OptimizedCallTarget) super.getCurrentCallTarget();
131    }
132
133    @Override
134    public OptimizedCallTarget getClonedCallTarget() {
135        return splitCallTarget;
136    }
137
138    private void onInterpreterCall(Object[] arguments) {
139        int calls = ++callCount;
140        if (calls == 1) {
141            getCurrentCallTarget().incrementKnownCallSites();
142        }
143        splittingStrategy.beforeCall(arguments);
144    }
145
146    /** Used by the splitting strategy to install new targets. */
147    public void installSplitCallTarget(OptimizedCallTarget newTarget) {
148        CompilerAsserts.neverPartOfCompilation();
149
150        OptimizedCallTarget currentTarget = getCurrentCallTarget();
151        if (currentTarget == newTarget) {
152            return;
153        }
154
155        if (callCount >= 1) {
156            currentTarget.decrementKnownCallSites();
157        }
158        newTarget.incrementKnownCallSites();
159
160        // dummy replace to report the split
161        replace(this, "Split call " + newTarget.toString());
162        if (newTarget.getSourceCallTarget() == null) {
163            splitCallTarget = null;
164        } else {
165            splitCallTarget = newTarget;
166        }
167        runtime.getCompilationNotify().notifyCompilationSplit(this);
168    }
169
170    @Override
171    public boolean cloneCallTarget() {
172        splittingStrategy.forceSplitting();
173        return true;
174    }
175
176}