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