1 /* 2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.phases.common.inlining.info.elem; 24 25 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Optional; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 import org.graalvm.compiler.core.common.type.Stamp; 31 import org.graalvm.compiler.debug.DebugContext; 32 import org.graalvm.compiler.graph.Node; 33 import org.graalvm.compiler.graph.NodeInputList; 34 import org.graalvm.compiler.nodes.ConstantNode; 35 import org.graalvm.compiler.nodes.Invoke; 36 import org.graalvm.compiler.nodes.ParameterNode; 37 import org.graalvm.compiler.nodes.StructuredGraph; 38 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 39 import org.graalvm.compiler.nodes.ValueNode; 40 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 41 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; 42 import org.graalvm.compiler.phases.common.inlining.InliningUtil; 43 import org.graalvm.compiler.phases.graph.FixedNodeProbabilityCache; 44 import org.graalvm.compiler.phases.tiers.HighTierContext; 45 46 import jdk.vm.ci.meta.Constant; 47 import jdk.vm.ci.meta.ResolvedJavaMethod; 48 49 /** 50 * <p> 51 * Represents a feasible concrete target for inlining, whose graph has been copied already and thus 52 * can be modified without affecting the original (usually cached) version. 53 * </p> 54 * 55 * <p> 56 * Instances of this class don't make sense in isolation but as part of an 57 * {@link org.graalvm.compiler.phases.common.inlining.info.InlineInfo InlineInfo}. 58 * </p> 59 * 60 * @see org.graalvm.compiler.phases.common.inlining.walker.InliningData#moveForward() 61 * @see org.graalvm.compiler.phases.common.inlining.walker.CallsiteHolderExplorable 62 */ 63 public class InlineableGraph implements Inlineable { 64 65 private final StructuredGraph graph; 66 67 private FixedNodeProbabilityCache probabilites = new FixedNodeProbabilityCache(); 68 69 public InlineableGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { 70 StructuredGraph original = getOriginalGraph(method, context, canonicalizer, invoke.asNode().graph(), invoke.bci()); 71 // TODO copying the graph is only necessary if it is modified or if it contains any invokes 72 this.graph = (StructuredGraph) original.copy(invoke.asNode().getDebug()); 73 specializeGraphToArguments(invoke, context, canonicalizer); 74 } 75 76 /** 77 * This method looks up in a cache the graph for the argument, if not found bytecode is parsed. 78 * The graph thus obtained is returned, ie the caller is responsible for cloning before 79 * modification. 80 */ 81 private static StructuredGraph getOriginalGraph(final ResolvedJavaMethod method, final HighTierContext context, CanonicalizerPhase canonicalizer, StructuredGraph caller, int callerBci) { 82 StructuredGraph result = InliningUtil.getIntrinsicGraph(context.getReplacements(), method, callerBci); 83 if (result != null) { 84 return result; 85 } 86 return parseBytecodes(method, context, canonicalizer, caller); 87 } 88 89 /** 90 * @return true iff one or more parameters <code>newGraph</code> were specialized to account for 91 * a constant argument, or an argument with a more specific stamp. 92 */ 93 @SuppressWarnings("try") 94 private boolean specializeGraphToArguments(final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { 95 DebugContext debug = graph.getDebug(); 96 try (DebugContext.Scope s = debug.scope("InlineGraph", graph)) { 97 98 ArrayList<Node> parameterUsages = replaceParamsWithMoreInformativeArguments(invoke, context); 99 if (parameterUsages != null) { 100 assert !parameterUsages.isEmpty() : "The caller didn't have more information about arguments after all"; 101 canonicalizer.applyIncremental(graph, context, parameterUsages); 102 return true; 103 } else { 104 // TODO (chaeubl): if args are not more concrete, inlining should be avoided 105 // in most cases or we could at least use the previous graph size + invoke 106 // probability to check the inlining 107 return false; 108 } 109 110 } catch (Throwable e) { 111 throw debug.handle(e); 112 } 113 } 114 115 private static boolean isArgMoreInformativeThanParam(ValueNode arg, ParameterNode param) { 116 return arg.isConstant() || canStampBeImproved(arg, param); 117 } 118 119 private static boolean canStampBeImproved(ValueNode arg, ParameterNode param) { 120 return improvedStamp(arg, param) != null; 121 } 122 123 private static Stamp improvedStamp(ValueNode arg, ParameterNode param) { 124 Stamp joinedStamp = param.stamp().join(arg.stamp()); 125 if (joinedStamp == null || joinedStamp.equals(param.stamp())) { 126 return null; 127 } 128 return joinedStamp; 129 } 130 131 /** 132 * This method detects: 133 * <ul> 134 * <li>constants among the arguments to the <code>invoke</code></li> 135 * <li>arguments with more precise type than that declared by the corresponding parameter</li> 136 * </ul> 137 * 138 * <p> 139 * The corresponding parameters are updated to reflect the above information. Before doing so, 140 * their usages are added to <code>parameterUsages</code> for later incremental 141 * canonicalization. 142 * </p> 143 * 144 * @return null if no incremental canonicalization is need, a list of nodes for such 145 * canonicalization otherwise. 146 */ 147 private ArrayList<Node> replaceParamsWithMoreInformativeArguments(final Invoke invoke, final HighTierContext context) { 148 NodeInputList<ValueNode> args = invoke.callTarget().arguments(); 149 ArrayList<Node> parameterUsages = null; 150 List<ParameterNode> params = graph.getNodes(ParameterNode.TYPE).snapshot(); 151 assert params.size() <= args.size(); 152 /* 153 * param-nodes that aren't used (eg, as a result of canonicalization) don't occur in 154 * `params`. Thus, in general, the sizes of `params` and `args` don't always match. Still, 155 * it's always possible to pair a param-node with its corresponding arg-node using 156 * param.index() as index into `args`. 157 */ 158 for (ParameterNode param : params) { 159 if (param.usages().isNotEmpty()) { 160 ValueNode arg = args.get(param.index()); 161 if (arg.isConstant()) { 162 Constant constant = arg.asConstant(); 163 parameterUsages = trackParameterUsages(param, parameterUsages); 164 // collect param usages before replacing the param 165 param.replaceAtUsagesAndDelete(graph.unique( 166 ConstantNode.forConstant(arg.stamp(), constant, ((ConstantNode) arg).getStableDimension(), ((ConstantNode) arg).isDefaultStable(), context.getMetaAccess()))); 167 // param-node gone, leaving a gap in the sequence given by param.index() 168 } else { 169 Stamp impro = improvedStamp(arg, param); 170 if (impro != null) { 171 param.setStamp(impro); 172 parameterUsages = trackParameterUsages(param, parameterUsages); 173 } else { 174 assert !isArgMoreInformativeThanParam(arg, param); 175 } 176 } 177 } 178 } 179 assert (parameterUsages == null) || (!parameterUsages.isEmpty()); 180 return parameterUsages; 181 } 182 183 private static ArrayList<Node> trackParameterUsages(ParameterNode param, ArrayList<Node> parameterUsages) { 184 ArrayList<Node> result = (parameterUsages == null) ? new ArrayList<>() : parameterUsages; 185 param.usages().snapshotTo(result); 186 return result; 187 } 188 189 /** 190 * This method builds the IR nodes for the given <code>method</code> and canonicalizes them. 191 * Provided profiling info is mature, the resulting graph is cached. The caller is responsible 192 * for cloning before modification. 193 * </p> 194 */ 195 @SuppressWarnings("try") 196 private static StructuredGraph parseBytecodes(ResolvedJavaMethod method, HighTierContext context, CanonicalizerPhase canonicalizer, StructuredGraph caller) { 197 DebugContext debug = caller.getDebug(); 198 StructuredGraph newGraph = new StructuredGraph.Builder(caller.getOptions(), debug, AllowAssumptions.ifNonNull(caller.getAssumptions())).method(method).build(); 199 try (DebugContext.Scope s = debug.scope("InlineGraph", newGraph)) { 200 if (!caller.isUnsafeAccessTrackingEnabled()) { 201 newGraph.disableUnsafeAccessTracking(); 202 } 203 if (context.getGraphBuilderSuite() != null) { 204 context.getGraphBuilderSuite().apply(newGraph, context); 205 } 206 assert newGraph.start().next() != null : "graph needs to be populated by the GraphBuilderSuite " + method + ", " + method.canBeInlined(); 207 208 new DeadCodeEliminationPhase(Optional).apply(newGraph); 209 210 canonicalizer.apply(newGraph, context); 211 212 return newGraph; 213 } catch (Throwable e) { 214 throw debug.handle(e); 215 } 216 } 217 218 @Override 219 public int getNodeCount() { 220 return InliningUtil.getNodeCount(graph); 221 } 222 223 @Override 224 public Iterable<Invoke> getInvokes() { 225 return graph.getInvokes(); 226 } 227 228 @Override 229 public double getProbability(Invoke invoke) { 230 return probabilites.applyAsDouble(invoke.asNode()); 231 } 232 233 public StructuredGraph getGraph() { 234 return graph; 235 } 236 }