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