001/* 002 * Copyright (c) 2011, 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.phases.common.inlining.walker; 024 025import static com.oracle.graal.compiler.common.GraalOptions.*; 026 027import java.util.*; 028 029import jdk.internal.jvmci.code.*; 030import jdk.internal.jvmci.common.*; 031import com.oracle.graal.debug.*; 032import jdk.internal.jvmci.meta.*; 033import jdk.internal.jvmci.meta.Assumptions.*; 034 035import com.oracle.graal.compiler.common.type.*; 036import com.oracle.graal.graph.*; 037import com.oracle.graal.nodes.*; 038import com.oracle.graal.nodes.java.*; 039import com.oracle.graal.nodes.virtual.*; 040import com.oracle.graal.phases.*; 041import com.oracle.graal.phases.common.*; 042import com.oracle.graal.phases.common.inlining.*; 043import com.oracle.graal.phases.common.inlining.info.*; 044import com.oracle.graal.phases.common.inlining.info.elem.*; 045import com.oracle.graal.phases.common.inlining.policy.*; 046import com.oracle.graal.phases.tiers.*; 047import com.oracle.graal.phases.util.*; 048 049/** 050 * <p> 051 * The space of inlining decisions is explored depth-first with the help of a stack realized by 052 * {@link InliningData}. At any point in time, the topmost element of that stack consists of: 053 * <ul> 054 * <li>the callsite under consideration is tracked as a {@link MethodInvocation}.</li> 055 * <li> 056 * one or more {@link CallsiteHolder}s, all of them associated to the callsite above. Why more than 057 * one? Depending on the type-profile for the receiver more than one concrete method may be feasible 058 * target.</li> 059 * </ul> 060 * </p> 061 * 062 * <p> 063 * The bottom element in the stack consists of: 064 * <ul> 065 * <li> 066 * a single {@link MethodInvocation} (the 067 * {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#isRoot root} one, ie the 068 * unknown caller of the root graph)</li> 069 * <li> 070 * a single {@link CallsiteHolder} (the root one, for the method on which inlining was called)</li> 071 * </ul> 072 * </p> 073 * 074 * @see #moveForward() 075 */ 076public class InliningData { 077 078 // Metrics 079 private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); 080 private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); 081 private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); 082 083 /** 084 * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. 085 */ 086 private final ArrayDeque<CallsiteHolder> graphQueue = new ArrayDeque<>(); 087 private final ArrayDeque<MethodInvocation> invocationQueue = new ArrayDeque<>(); 088 089 private final HighTierContext context; 090 private final int maxMethodPerInlining; 091 private final CanonicalizerPhase canonicalizer; 092 private final InliningPolicy inliningPolicy; 093 094 private int maxGraphs; 095 096 public InliningData(StructuredGraph rootGraph, HighTierContext context, int maxMethodPerInlining, CanonicalizerPhase canonicalizer, InliningPolicy inliningPolicy) { 097 assert rootGraph != null; 098 this.context = context; 099 this.maxMethodPerInlining = maxMethodPerInlining; 100 this.canonicalizer = canonicalizer; 101 this.inliningPolicy = inliningPolicy; 102 this.maxGraphs = 1; 103 104 invocationQueue.push(new MethodInvocation(null, 1.0, 1.0, null)); 105 graphQueue.push(new CallsiteHolderExplorable(rootGraph, 1.0, 1.0, null)); 106 } 107 108 public static boolean isFreshInstantiation(ValueNode arg) { 109 return (arg instanceof AbstractNewObjectNode) || (arg instanceof AllocatedObjectNode) || (arg instanceof VirtualObjectNode); 110 } 111 112 private String checkTargetConditionsHelper(ResolvedJavaMethod method, int invokeBci) { 113 if (method == null) { 114 return "the method is not resolved"; 115 } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(context.getReplacements(), method, invokeBci))) { 116 return "it is a non-intrinsic native method"; 117 } else if (method.isAbstract()) { 118 return "it is an abstract method"; 119 } else if (!method.getDeclaringClass().isInitialized()) { 120 return "the method's class is not initialized"; 121 } else if (!method.canBeInlined()) { 122 return "it is marked non-inlinable"; 123 } else if (countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) { 124 return "it exceeds the maximum recursive inlining depth"; 125 } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(context.getOptimisticOptimizations())) { 126 return "the callee uses less optimistic optimizations than caller"; 127 } else { 128 return null; 129 } 130 } 131 132 private boolean checkTargetConditions(Invoke invoke, ResolvedJavaMethod method) { 133 final String failureMessage = checkTargetConditionsHelper(method, invoke.bci()); 134 if (failureMessage == null) { 135 return true; 136 } else { 137 InliningUtil.logNotInlined(invoke, inliningDepth(), method, failureMessage); 138 return false; 139 } 140 } 141 142 /** 143 * Determines if inlining is possible at the given invoke node. 144 * 145 * @param invoke the invoke that should be inlined 146 * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke 147 */ 148 private InlineInfo getInlineInfo(Invoke invoke) { 149 final String failureMessage = InliningUtil.checkInvokeConditions(invoke); 150 if (failureMessage != null) { 151 InliningUtil.logNotInlinedMethod(invoke, failureMessage); 152 return null; 153 } 154 MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); 155 ResolvedJavaMethod targetMethod = callTarget.targetMethod(); 156 157 if (callTarget.invokeKind() == CallTargetNode.InvokeKind.Special || targetMethod.canBeStaticallyBound()) { 158 return getExactInlineInfo(invoke, targetMethod); 159 } 160 161 assert callTarget.invokeKind().isIndirect(); 162 163 ResolvedJavaType holder = targetMethod.getDeclaringClass(); 164 if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) { 165 return null; 166 } 167 ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(); 168 if (receiverStamp.alwaysNull()) { 169 // Don't inline if receiver is known to be null 170 return null; 171 } 172 ResolvedJavaType contextType = invoke.getContextType(); 173 if (receiverStamp.type() != null) { 174 // the invoke target might be more specific than the holder (happens after inlining: 175 // parameters lose their declared type...) 176 ResolvedJavaType receiverType = receiverStamp.type(); 177 if (receiverType != null && holder.isAssignableFrom(receiverType)) { 178 holder = receiverType; 179 if (receiverStamp.isExactType()) { 180 assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; 181 ResolvedJavaMethod resolvedMethod = holder.resolveConcreteMethod(targetMethod, contextType); 182 if (resolvedMethod != null) { 183 return getExactInlineInfo(invoke, resolvedMethod); 184 } 185 } 186 } 187 } 188 189 if (holder.isArray()) { 190 // arrays can be treated as Objects 191 ResolvedJavaMethod resolvedMethod = holder.resolveConcreteMethod(targetMethod, contextType); 192 if (resolvedMethod != null) { 193 return getExactInlineInfo(invoke, resolvedMethod); 194 } 195 } 196 197 if (callTarget.graph().getAssumptions() != null) { 198 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = holder.findLeafConcreteSubtype(); 199 if (leafConcreteSubtype != null) { 200 ResolvedJavaMethod resolvedMethod = leafConcreteSubtype.getResult().resolveConcreteMethod(targetMethod, contextType); 201 if (resolvedMethod != null) { 202 return getAssumptionInlineInfo(invoke, resolvedMethod, leafConcreteSubtype); 203 } 204 } 205 206 AssumptionResult<ResolvedJavaMethod> concrete = holder.findUniqueConcreteMethod(targetMethod); 207 if (concrete != null) { 208 return getAssumptionInlineInfo(invoke, concrete.getResult(), concrete); 209 } 210 } 211 212 // type check based inlining 213 return getTypeCheckedInlineInfo(invoke, targetMethod); 214 } 215 216 private InlineInfo getTypeCheckedInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) { 217 JavaTypeProfile typeProfile = ((MethodCallTargetNode) invoke.callTarget()).getProfile(); 218 if (typeProfile == null) { 219 InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no type profile exists"); 220 return null; 221 } 222 223 JavaTypeProfile.ProfiledType[] ptypes = typeProfile.getTypes(); 224 if (ptypes == null || ptypes.length <= 0) { 225 InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no types in profile"); 226 return null; 227 } 228 ResolvedJavaType contextType = invoke.getContextType(); 229 double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); 230 final OptimisticOptimizations optimisticOpts = context.getOptimisticOptimizations(); 231 if (ptypes.length == 1 && notRecordedTypeProbability == 0) { 232 if (!optimisticOpts.inlineMonomorphicCalls()) { 233 InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "inlining monomorphic calls is disabled"); 234 return null; 235 } 236 237 ResolvedJavaType type = ptypes[0].getType(); 238 assert type.isArray() || type.isConcrete(); 239 ResolvedJavaMethod concrete = type.resolveConcreteMethod(targetMethod, contextType); 240 if (!checkTargetConditions(invoke, concrete)) { 241 return null; 242 } 243 return new TypeGuardInlineInfo(invoke, concrete, type); 244 } else { 245 invoke.setPolymorphic(true); 246 247 if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { 248 InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length); 249 return null; 250 } 251 if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { 252 // due to filtering impossible types, notRecordedTypeProbability can be > 0 although 253 // the number of types is lower than what can be recorded in a type profile 254 InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length, 255 notRecordedTypeProbability * 100); 256 return null; 257 } 258 259 // Find unique methods and their probabilities. 260 ArrayList<ResolvedJavaMethod> concreteMethods = new ArrayList<>(); 261 ArrayList<Double> concreteMethodsProbabilities = new ArrayList<>(); 262 for (int i = 0; i < ptypes.length; i++) { 263 ResolvedJavaMethod concrete = ptypes[i].getType().resolveConcreteMethod(targetMethod, contextType); 264 if (concrete == null) { 265 InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "could not resolve method"); 266 return null; 267 } 268 int index = concreteMethods.indexOf(concrete); 269 double curProbability = ptypes[i].getProbability(); 270 if (index < 0) { 271 index = concreteMethods.size(); 272 concreteMethods.add(concrete); 273 concreteMethodsProbabilities.add(curProbability); 274 } else { 275 concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability); 276 } 277 } 278 279 // Clear methods that fall below the threshold. 280 if (notRecordedTypeProbability > 0) { 281 ArrayList<ResolvedJavaMethod> newConcreteMethods = new ArrayList<>(); 282 ArrayList<Double> newConcreteMethodsProbabilities = new ArrayList<>(); 283 for (int i = 0; i < concreteMethods.size(); ++i) { 284 if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) { 285 newConcreteMethods.add(concreteMethods.get(i)); 286 newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i)); 287 } 288 } 289 290 if (newConcreteMethods.isEmpty()) { 291 // No method left that is worth inlining. 292 InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)", 293 concreteMethods.size()); 294 return null; 295 } 296 297 concreteMethods = newConcreteMethods; 298 concreteMethodsProbabilities = newConcreteMethodsProbabilities; 299 } 300 301 if (concreteMethods.size() > maxMethodPerInlining) { 302 InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxMethodPerInlining); 303 return null; 304 } 305 306 // Clean out types whose methods are no longer available. 307 ArrayList<JavaTypeProfile.ProfiledType> usedTypes = new ArrayList<>(); 308 ArrayList<Integer> typesToConcretes = new ArrayList<>(); 309 for (JavaTypeProfile.ProfiledType type : ptypes) { 310 ResolvedJavaMethod concrete = type.getType().resolveConcreteMethod(targetMethod, contextType); 311 int index = concreteMethods.indexOf(concrete); 312 if (index == -1) { 313 notRecordedTypeProbability += type.getProbability(); 314 } else { 315 assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete; 316 usedTypes.add(type); 317 typesToConcretes.add(index); 318 } 319 } 320 321 if (usedTypes.isEmpty()) { 322 // No type left that is worth checking for. 323 InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length); 324 return null; 325 } 326 327 for (ResolvedJavaMethod concrete : concreteMethods) { 328 if (!checkTargetConditions(invoke, concrete)) { 329 InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); 330 return null; 331 } 332 } 333 return new MultiTypeGuardInlineInfo(invoke, concreteMethods, usedTypes, typesToConcretes, notRecordedTypeProbability); 334 } 335 } 336 337 private InlineInfo getAssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, AssumptionResult<?> takenAssumption) { 338 assert concrete.isConcrete(); 339 if (checkTargetConditions(invoke, concrete)) { 340 return new AssumptionInlineInfo(invoke, concrete, takenAssumption); 341 } 342 return null; 343 } 344 345 private InlineInfo getExactInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) { 346 assert targetMethod.isConcrete(); 347 if (checkTargetConditions(invoke, targetMethod)) { 348 return new ExactInlineInfo(invoke, targetMethod); 349 } 350 return null; 351 } 352 353 private void doInline(CallsiteHolderExplorable callerCallsiteHolder, MethodInvocation calleeInvocation) { 354 StructuredGraph callerGraph = callerCallsiteHolder.graph(); 355 InlineInfo calleeInfo = calleeInvocation.callee(); 356 try { 357 try (Debug.Scope scope = Debug.scope("doInline", callerGraph)) { 358 Set<Node> canonicalizedNodes = Node.newSet(); 359 calleeInfo.invoke().asNode().usages().snapshotTo(canonicalizedNodes); 360 Collection<Node> parameterUsages = calleeInfo.inline(new Providers(context)); 361 canonicalizedNodes.addAll(parameterUsages); 362 metricInliningRuns.increment(); 363 Debug.dump(callerGraph, "after %s", calleeInfo); 364 365 if (OptCanonicalizer.getValue()) { 366 Graph.Mark markBeforeCanonicalization = callerGraph.getMark(); 367 368 canonicalizer.applyIncremental(callerGraph, context, canonicalizedNodes); 369 370 // process invokes that are possibly created during canonicalization 371 for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { 372 if (newNode instanceof Invoke) { 373 callerCallsiteHolder.pushInvoke((Invoke) newNode); 374 } 375 } 376 } 377 378 callerCallsiteHolder.computeProbabilities(); 379 380 metricInliningPerformed.increment(); 381 } 382 } catch (BailoutException bailout) { 383 throw bailout; 384 } catch (AssertionError | RuntimeException e) { 385 throw new JVMCIError(e).addContext(calleeInfo.toString()); 386 } catch (JVMCIError e) { 387 throw e.addContext(calleeInfo.toString()); 388 } catch (Throwable e) { 389 throw Debug.handle(e); 390 } 391 } 392 393 /** 394 * 395 * This method attempts: 396 * <ol> 397 * <li> 398 * to inline at the callsite given by <code>calleeInvocation</code>, where that callsite belongs 399 * to the {@link CallsiteHolderExplorable} at the top of the {@link #graphQueue} maintained in 400 * this class.</li> 401 * <li> 402 * otherwise, to devirtualize the callsite in question.</li> 403 * </ol> 404 * 405 * @return true iff inlining was actually performed 406 */ 407 private boolean tryToInline(MethodInvocation calleeInvocation, int inliningDepth) { 408 CallsiteHolderExplorable callerCallsiteHolder = (CallsiteHolderExplorable) currentGraph(); 409 InlineInfo calleeInfo = calleeInvocation.callee(); 410 assert callerCallsiteHolder.containsInvoke(calleeInfo.invoke()); 411 metricInliningConsidered.increment(); 412 413 if (inliningPolicy.isWorthInlining(context.getReplacements(), calleeInvocation, inliningDepth, true)) { 414 doInline(callerCallsiteHolder, calleeInvocation); 415 return true; 416 } 417 418 if (context.getOptimisticOptimizations().devirtualizeInvokes()) { 419 calleeInfo.tryToDevirtualizeInvoke(new Providers(context)); 420 } 421 422 return false; 423 } 424 425 /** 426 * This method picks one of the callsites belonging to the current 427 * {@link CallsiteHolderExplorable}. Provided the callsite qualifies to be analyzed for 428 * inlining, this method prepares a new stack top in {@link InliningData} for such callsite, 429 * which comprises: 430 * <ul> 431 * <li>preparing a summary of feasible targets, ie preparing an {@link InlineInfo}</li> 432 * <li>based on it, preparing the stack top proper which consists of:</li> 433 * <ul> 434 * <li>one {@link MethodInvocation}</li> 435 * <li>a {@link CallsiteHolder} for each feasible target</li> 436 * </ul> 437 * </ul> 438 * 439 * <p> 440 * The thus prepared "stack top" is needed by {@link #moveForward()} to explore the space of 441 * inlining decisions (each decision one of: backtracking, delving, inlining). 442 * </p> 443 * 444 * <p> 445 * The {@link InlineInfo} used to get things rolling is kept around in the 446 * {@link MethodInvocation}, it will be needed in case of inlining, see 447 * {@link InlineInfo#inline(Providers)} 448 * </p> 449 */ 450 private void processNextInvoke() { 451 CallsiteHolderExplorable callsiteHolder = (CallsiteHolderExplorable) currentGraph(); 452 Invoke invoke = callsiteHolder.popInvoke(); 453 InlineInfo info = getInlineInfo(invoke); 454 455 if (info != null) { 456 info.populateInlinableElements(context, currentGraph().graph(), canonicalizer); 457 double invokeProbability = callsiteHolder.invokeProbability(invoke); 458 double invokeRelevance = callsiteHolder.invokeRelevance(invoke); 459 MethodInvocation methodInvocation = new MethodInvocation(info, invokeProbability, invokeRelevance, freshlyInstantiatedArguments(invoke, callsiteHolder.getFixedParams())); 460 pushInvocationAndGraphs(methodInvocation); 461 } 462 } 463 464 /** 465 * Gets the freshly instantiated arguments. 466 * <p> 467 * A freshly instantiated argument is either: 468 * <uL> 469 * <li>an {@link InliningData#isFreshInstantiation(com.oracle.graal.nodes.ValueNode)}</li> 470 * <li>a fixed-param, ie a {@link ParameterNode} receiving a freshly instantiated argument</li> 471 * </uL> 472 * </p> 473 * 474 * @return the positions of freshly instantiated arguments in the argument list of the 475 * <code>invoke</code>, or null if no such positions exist. 476 */ 477 public static BitSet freshlyInstantiatedArguments(Invoke invoke, Set<ParameterNode> fixedParams) { 478 assert fixedParams != null; 479 assert paramsAndInvokeAreInSameGraph(invoke, fixedParams); 480 BitSet result = null; 481 int argIdx = 0; 482 for (ValueNode arg : invoke.callTarget().arguments()) { 483 assert arg != null; 484 if (isFreshInstantiation(arg) || fixedParams.contains(arg)) { 485 if (result == null) { 486 result = new BitSet(); 487 } 488 result.set(argIdx); 489 } 490 argIdx++; 491 } 492 return result; 493 } 494 495 private static boolean paramsAndInvokeAreInSameGraph(Invoke invoke, Set<ParameterNode> fixedParams) { 496 if (fixedParams.isEmpty()) { 497 return true; 498 } 499 for (ParameterNode p : fixedParams) { 500 if (p.graph() != invoke.asNode().graph()) { 501 return false; 502 } 503 } 504 return true; 505 } 506 507 public int graphCount() { 508 return graphQueue.size(); 509 } 510 511 public boolean hasUnprocessedGraphs() { 512 return !graphQueue.isEmpty(); 513 } 514 515 private CallsiteHolder currentGraph() { 516 return graphQueue.peek(); 517 } 518 519 private void popGraph() { 520 graphQueue.pop(); 521 assert graphQueue.size() <= maxGraphs; 522 } 523 524 private void popGraphs(int count) { 525 assert count >= 0; 526 for (int i = 0; i < count; i++) { 527 graphQueue.pop(); 528 } 529 } 530 531 private static final Object[] NO_CONTEXT = {}; 532 533 /** 534 * Gets the call hierarchy of this inlining from outer most call to inner most callee. 535 */ 536 private Object[] inliningContext() { 537 if (!Debug.isDumpEnabled()) { 538 return NO_CONTEXT; 539 } 540 Object[] result = new Object[graphQueue.size()]; 541 int i = 0; 542 for (CallsiteHolder g : graphQueue) { 543 result[i++] = g.method(); 544 } 545 return result; 546 } 547 548 private MethodInvocation currentInvocation() { 549 return invocationQueue.peekFirst(); 550 } 551 552 private void pushInvocationAndGraphs(MethodInvocation methodInvocation) { 553 invocationQueue.addFirst(methodInvocation); 554 InlineInfo info = methodInvocation.callee(); 555 maxGraphs += info.numberOfMethods(); 556 assert graphQueue.size() <= maxGraphs; 557 for (int i = 0; i < info.numberOfMethods(); i++) { 558 CallsiteHolder ch = methodInvocation.buildCallsiteHolderForElement(i); 559 assert !contains(ch.graph()); 560 graphQueue.push(ch); 561 assert graphQueue.size() <= maxGraphs; 562 } 563 } 564 565 private void popInvocation() { 566 maxGraphs -= invocationQueue.peekFirst().callee().numberOfMethods(); 567 assert graphQueue.size() <= maxGraphs; 568 invocationQueue.removeFirst(); 569 } 570 571 public int countRecursiveInlining(ResolvedJavaMethod method) { 572 int count = 0; 573 for (CallsiteHolder callsiteHolder : graphQueue) { 574 if (method.equals(callsiteHolder.method())) { 575 count++; 576 } 577 } 578 return count; 579 } 580 581 public int inliningDepth() { 582 assert invocationQueue.size() > 0; 583 return invocationQueue.size() - 1; 584 } 585 586 @Override 587 public String toString() { 588 StringBuilder result = new StringBuilder("Invocations: "); 589 590 for (MethodInvocation invocation : invocationQueue) { 591 if (invocation.callee() != null) { 592 result.append(invocation.callee().numberOfMethods()); 593 result.append("x "); 594 result.append(invocation.callee().invoke()); 595 result.append("; "); 596 } 597 } 598 599 result.append("\nGraphs: "); 600 for (CallsiteHolder graph : graphQueue) { 601 result.append(graph.graph()); 602 result.append("; "); 603 } 604 605 return result.toString(); 606 } 607 608 private boolean contains(StructuredGraph graph) { 609 assert graph != null; 610 for (CallsiteHolder info : graphQueue) { 611 if (info.graph() == graph) { 612 return true; 613 } 614 } 615 return false; 616 } 617 618 /** 619 * <p> 620 * The stack realized by {@link InliningData} grows and shrinks as choices are made among the 621 * alternatives below: 622 * <ol> 623 * <li> 624 * not worth inlining: pop stack top, which comprises: 625 * <ul> 626 * <li>pop any remaining graphs not yet delved into</li> 627 * <li>pop the current invocation</li> 628 * </ul> 629 * </li> 630 * <li> 631 * {@link #processNextInvoke() delve} into one of the callsites hosted in the current graph, 632 * such callsite is explored next by {@link #moveForward()}</li> 633 * <li> 634 * {@link #tryToInline(MethodInvocation, int) try to inline}: move past the current graph 635 * (remove it from the topmost element). 636 * <ul> 637 * <li> 638 * If that was the last one then {@link #tryToInline(MethodInvocation, int) try to inline} the 639 * callsite under consideration (ie, the "current invocation").</li> 640 * <li> 641 * Whether inlining occurs or not, that callsite is removed from the top of {@link InliningData} 642 * .</li> 643 * </ul> 644 * </li> 645 * </ol> 646 * </p> 647 * 648 * <p> 649 * Some facts about the alternatives above: 650 * <ul> 651 * <li> 652 * the first step amounts to backtracking, the 2nd one to depth-search, and the 3rd one also 653 * involves backtracking (however possibly after inlining).</li> 654 * <li> 655 * the choice of abandon-and-backtrack or delve-into depends on 656 * {@link InliningPolicy#isWorthInlining} and {@link InliningPolicy#continueInlining}.</li> 657 * <li> 658 * the 3rd choice is picked whenever none of the previous choices are made</li> 659 * </ul> 660 * </p> 661 * 662 * @return true iff inlining was actually performed 663 */ 664 public boolean moveForward() { 665 666 final MethodInvocation currentInvocation = currentInvocation(); 667 668 final boolean backtrack = (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(context.getReplacements(), currentInvocation, inliningDepth(), false)); 669 if (backtrack) { 670 int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); 671 assert remainingGraphs > 0; 672 popGraphs(remainingGraphs); 673 popInvocation(); 674 return false; 675 } 676 677 final boolean delve = currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(currentGraph().graph()); 678 if (delve) { 679 processNextInvoke(); 680 return false; 681 } 682 683 popGraph(); 684 if (currentInvocation.isRoot()) { 685 return false; 686 } 687 688 // try to inline 689 assert currentInvocation.callee().invoke().asNode().isAlive(); 690 currentInvocation.incrementProcessedGraphs(); 691 if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { 692 /* 693 * "all of currentInvocation's graphs processed" amounts to 694 * "all concrete methods that come into question already had the callees they contain analyzed for inlining" 695 */ 696 popInvocation(); 697 try (Debug.Scope s = Debug.scope("Inlining", inliningContext())) { 698 return tryToInline(currentInvocation, inliningDepth() + 1); 699 } catch (Throwable e) { 700 throw Debug.handle(e); 701 } 702 } 703 704 return false; 705 } 706 707 /** 708 * Checks an invariant that {@link #moveForward()} must maintain: "the top invocation records 709 * how many concrete target methods (for it) remain on the {@link #graphQueue}; those targets 710 * 'belong' to the current invocation in question. 711 */ 712 private boolean topGraphsForTopInvocation() { 713 if (invocationQueue.isEmpty()) { 714 assert graphQueue.isEmpty(); 715 return true; 716 } 717 if (currentInvocation().isRoot()) { 718 if (!graphQueue.isEmpty()) { 719 assert graphQueue.size() == 1; 720 } 721 return true; 722 } 723 final int remainingGraphs = currentInvocation().totalGraphs() - currentInvocation().processedGraphs(); 724 final Iterator<CallsiteHolder> iter = graphQueue.iterator(); 725 for (int i = (remainingGraphs - 1); i >= 0; i--) { 726 if (!iter.hasNext()) { 727 assert false; 728 return false; 729 } 730 CallsiteHolder queuedTargetCH = iter.next(); 731 Inlineable targetIE = currentInvocation().callee().inlineableElementAt(i); 732 InlineableGraph targetIG = (InlineableGraph) targetIE; 733 assert queuedTargetCH.method().equals(targetIG.getGraph().method()); 734 } 735 return true; 736 } 737 738 /** 739 * This method checks invariants for this class. Named after shorthand for 740 * "internal representation is ok". 741 */ 742 public boolean repOK() { 743 assert topGraphsForTopInvocation(); 744 return true; 745 } 746}