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}