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 java.util.*;
026
027import com.oracle.truffle.api.*;
028import com.oracle.truffle.api.nodes.*;
029
030public class DefaultTruffleSplittingStrategyNew implements TruffleSplittingStrategy {
031
032    private final int splitStart;
033    private final OptimizedDirectCallNode call;
034    private final boolean splittingEnabled;
035    private boolean splittingForced;
036    private TruffleStamp argumentStamp;
037
038    public DefaultTruffleSplittingStrategyNew(OptimizedDirectCallNode call) {
039        this.call = call;
040        this.splitStart = TruffleCompilerOptions.TruffleSplittingStartCallCount.getValue();
041        this.splittingEnabled = isSplittingEnabled(call);
042        this.argumentStamp = DefaultTruffleStamp.getInstance();
043        if (TruffleCompilerOptions.TruffleSplittingAggressive.getValue()) {
044            splittingForced = true;
045        }
046    }
047
048    private static boolean isSplittingEnabled(OptimizedDirectCallNode call) {
049        if (!TruffleCompilerOptions.TruffleSplitting.getValue()) {
050            return false;
051        }
052        if (!call.isCallTargetCloningAllowed()) {
053            return false;
054        }
055        if (TruffleCompilerOptions.TruffleSplittingAggressive.getValue()) {
056            return true;
057        }
058        int size = call.getCallTarget().getNonTrivialNodeCount();
059        if (size > TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) {
060            return false;
061        }
062        return true;
063    }
064
065    public void forceSplitting() {
066        splittingForced = true;
067    }
068
069    public void beforeCall(Object[] arguments) {
070        newSplitting(arguments);
071    }
072
073    public void afterCall(Object returnValue) {
074    }
075
076    private void newSplitting(Object[] arguments) {
077        CompilerAsserts.neverPartOfCompilation();
078        OptimizedCallTarget currentTarget = call.getCurrentCallTarget();
079
080        if (splittingForced) {
081            if (!call.isCallTargetCloned()) {
082                call.installSplitCallTarget(currentTarget.cloneUninitialized());
083            }
084            return;
085        }
086
087        TruffleStamp oldStamp = argumentStamp;
088        TruffleStamp newStamp = oldStamp;
089        if (!oldStamp.isCompatible(arguments)) {
090            newStamp = oldStamp.joinValue(arguments);
091            assert newStamp != oldStamp;
092        }
093
094        int calls = call.getCallCount();
095
096        if (oldStamp != newStamp || calls == splitStart || !call.getCurrentCallTarget().getArgumentStamp().equals(newStamp)) {
097            currentTarget = runSplitIteration(oldStamp, newStamp, calls);
098            currentTarget.mergeArgumentStamp(newStamp);
099            argumentStamp = newStamp;
100            assert call.getCurrentCallTarget().getArgumentStamp().equals(newStamp);
101        }
102    }
103
104    private OptimizedCallTarget runSplitIteration(TruffleStamp oldProfile, TruffleStamp newProfile, int calls) {
105        OptimizedCallTarget currentTarget = call.getCurrentCallTarget();
106        if (!splittingEnabled || calls < splitStart) {
107            return currentTarget;
108        }
109
110        OptimizedCallTarget target = call.getCallTarget();
111        Map<TruffleStamp, OptimizedCallTarget> profiles = target.getSplitVersions();
112        OptimizedCallTarget newTarget = currentTarget;
113
114        if (!currentTarget.getArgumentStamp().equals(newProfile)) {
115            if (target.getArgumentStamp().equals(newProfile)) {
116                // the original target is compatible again.
117                // -> we can use the original call target.
118                newTarget = target;
119            } else if (currentTarget.getKnownCallSiteCount() == 1 && currentTarget.getArgumentStamp().equals(oldProfile)) {
120                // we are the only caller + the profile is not polluted by other call sites
121                // -> reuse the currentTarget but update the profile if necessary
122                newTarget = currentTarget;
123                if (currentTarget.getSourceCallTarget() != null) {
124                    profiles.remove(oldProfile);
125                    profiles.put(newProfile, newTarget);
126                }
127            } else {
128                newTarget = profiles.get(newProfile);
129                if (newTarget == null) {
130                    // in case no compatible target was found we need to split
131                    newTarget = target.cloneUninitialized();
132                    profiles.put(newProfile, newTarget);
133                }
134            }
135        }
136
137        call.installSplitCallTarget(newTarget);
138
139        cleanup(currentTarget);
140        return newTarget;
141    }
142
143    private static void cleanup(OptimizedCallTarget currentTarget) {
144        if (currentTarget.getKnownCallSiteCount() == 0 && currentTarget.getSourceCallTarget() != null) {
145            OptimizedCallTarget removed = currentTarget.getSourceCallTarget().getSplitVersions().remove(currentTarget.getArgumentStamp());
146            if (removed != null) {
147                disposeTarget(removed);
148            }
149        }
150    }
151
152    private static void disposeTarget(OptimizedCallTarget removed) {
153        removed.getRootNode().accept(new NodeVisitor() {
154            public boolean visit(Node node) {
155                if (node instanceof OptimizedDirectCallNode) {
156                    OptimizedDirectCallNode call = ((OptimizedDirectCallNode) node);
157                    call.getCurrentCallTarget().decrementKnownCallSites();
158                    cleanup(call.getCurrentCallTarget());
159                }
160                return true;
161            }
162        });
163
164    }
165
166}