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.nodes.*;
026import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
027
028public class DefaultTruffleSplittingStrategy implements TruffleSplittingStrategy {
029
030    private final OptimizedDirectCallNode call;
031
032    public DefaultTruffleSplittingStrategy(OptimizedDirectCallNode call) {
033        this.call = call;
034    }
035
036    public void beforeCall(Object[] arguments) {
037        if (call.getCallCount() == 2) {
038            if (shouldSplit()) {
039                forceSplitting();
040            }
041        }
042    }
043
044    public void forceSplitting() {
045        if (call.isCallTargetCloned()) {
046            return;
047        }
048        call.installSplitCallTarget(call.getCallTarget().cloneUninitialized());
049    }
050
051    public void afterCall(Object returnValue) {
052    }
053
054    private boolean shouldSplit() {
055        if (call.getClonedCallTarget() != null) {
056            return false;
057        }
058        if (!TruffleCompilerOptions.TruffleSplitting.getValue()) {
059            return false;
060        }
061        if (!call.isCallTargetCloningAllowed()) {
062            return false;
063        }
064        OptimizedCallTarget splitTarget = call.getCallTarget();
065        int nodeCount = splitTarget.getNonTrivialNodeCount();
066        if (nodeCount > TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) {
067            return false;
068        }
069
070        // disable recursive splitting for now
071        OptimizedCallTarget root = (OptimizedCallTarget) call.getRootNode().getCallTarget();
072        if (root == splitTarget || root.getSourceCallTarget() == splitTarget) {
073            // recursive call found
074            return false;
075        }
076
077        // max one child call and callCount > 2 and kind of small number of nodes
078        if (isMaxSingleCall(call)) {
079            return true;
080        }
081        return countPolymorphic(call) >= 1;
082    }
083
084    private static boolean isMaxSingleCall(OptimizedDirectCallNode call) {
085        return NodeUtil.countNodes(call.getCurrentCallTarget().getRootNode(), new NodeCountFilter() {
086            public boolean isCounted(Node node) {
087                return node instanceof DirectCallNode;
088            }
089        }) <= 1;
090    }
091
092    private static int countPolymorphic(OptimizedDirectCallNode call) {
093        return NodeUtil.countNodes(call.getCurrentCallTarget().getRootNode(), new NodeCountFilter() {
094            public boolean isCounted(Node node) {
095                NodeCost cost = node.getCost();
096                boolean polymorphic = cost == NodeCost.POLYMORPHIC || cost == NodeCost.MEGAMORPHIC;
097                return polymorphic;
098            }
099        });
100    }
101
102}