1 /* 2 * Copyright (c) 2012, 2015, 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; 24 25 import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile; 26 import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException; 27 import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining; 28 29 import java.lang.reflect.Constructor; 30 import java.util.ArrayDeque; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.function.Consumer; 34 35 import org.graalvm.compiler.api.replacements.MethodSubstitution; 36 import org.graalvm.compiler.core.common.GraalOptions; 37 import org.graalvm.compiler.core.common.type.Stamp; 38 import org.graalvm.compiler.core.common.type.StampFactory; 39 import org.graalvm.compiler.core.common.type.TypeReference; 40 import org.graalvm.compiler.core.common.util.Util; 41 import org.graalvm.compiler.debug.DebugContext; 42 import org.graalvm.compiler.debug.GraalError; 43 import org.graalvm.compiler.graph.GraalGraphError; 44 import org.graalvm.compiler.graph.Graph; 45 import org.graalvm.compiler.graph.Graph.DuplicationReplacement; 46 import org.graalvm.compiler.graph.Graph.NodeEventScope; 47 import org.graalvm.compiler.graph.Node; 48 import org.graalvm.compiler.graph.NodeInputList; 49 import org.graalvm.compiler.graph.NodeMap; 50 import org.graalvm.compiler.graph.NodeSourcePosition; 51 import org.graalvm.compiler.graph.NodeWorkList; 52 import org.graalvm.compiler.nodeinfo.Verbosity; 53 import org.graalvm.compiler.nodes.AbstractBeginNode; 54 import org.graalvm.compiler.nodes.AbstractEndNode; 55 import org.graalvm.compiler.nodes.AbstractMergeNode; 56 import org.graalvm.compiler.nodes.BeginNode; 57 import org.graalvm.compiler.nodes.CallTargetNode; 58 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; 59 import org.graalvm.compiler.nodes.DeoptimizeNode; 60 import org.graalvm.compiler.nodes.EndNode; 61 import org.graalvm.compiler.nodes.FixedGuardNode; 62 import org.graalvm.compiler.nodes.FixedNode; 63 import org.graalvm.compiler.nodes.FixedWithNextNode; 64 import org.graalvm.compiler.nodes.FrameState; 65 import org.graalvm.compiler.nodes.Invoke; 66 import org.graalvm.compiler.nodes.InvokeNode; 67 import org.graalvm.compiler.nodes.InvokeWithExceptionNode; 68 import org.graalvm.compiler.nodes.KillingBeginNode; 69 import org.graalvm.compiler.nodes.LogicNode; 70 import org.graalvm.compiler.nodes.MergeNode; 71 import org.graalvm.compiler.nodes.ParameterNode; 72 import org.graalvm.compiler.nodes.PhiNode; 73 import org.graalvm.compiler.nodes.PiNode; 74 import org.graalvm.compiler.nodes.ReturnNode; 75 import org.graalvm.compiler.nodes.StartNode; 76 import org.graalvm.compiler.nodes.StateSplit; 77 import org.graalvm.compiler.nodes.StructuredGraph; 78 import org.graalvm.compiler.nodes.UnwindNode; 79 import org.graalvm.compiler.nodes.ValueNode; 80 import org.graalvm.compiler.nodes.calc.IsNullNode; 81 import org.graalvm.compiler.nodes.extended.ForeignCallNode; 82 import org.graalvm.compiler.nodes.extended.GuardingNode; 83 import org.graalvm.compiler.nodes.java.ExceptionObjectNode; 84 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 85 import org.graalvm.compiler.nodes.java.MonitorExitNode; 86 import org.graalvm.compiler.nodes.java.MonitorIdNode; 87 import org.graalvm.compiler.nodes.spi.Replacements; 88 import org.graalvm.compiler.nodes.type.StampTool; 89 import org.graalvm.compiler.nodes.util.GraphUtil; 90 import org.graalvm.compiler.phases.common.inlining.info.InlineInfo; 91 import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener; 92 import org.graalvm.compiler.phases.util.ValueMergeUtil; 93 import org.graalvm.util.EconomicMap; 94 import org.graalvm.util.EconomicSet; 95 import org.graalvm.util.Equivalence; 96 import org.graalvm.util.UnmodifiableEconomicMap; 97 import org.graalvm.util.UnmodifiableMapCursor; 98 99 import jdk.vm.ci.code.BytecodeFrame; 100 import jdk.vm.ci.meta.Assumptions; 101 import jdk.vm.ci.meta.DeoptimizationAction; 102 import jdk.vm.ci.meta.DeoptimizationReason; 103 import jdk.vm.ci.meta.JavaConstant; 104 import jdk.vm.ci.meta.JavaKind; 105 import jdk.vm.ci.meta.ResolvedJavaMethod; 106 import jdk.vm.ci.meta.ResolvedJavaType; 107 108 public class InliningUtil extends ValueMergeUtil { 109 110 private static final String inliningDecisionsScopeString = "InliningDecisions"; 111 112 /** 113 * Print a HotSpot-style inlining message to the console. 114 */ 115 private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) { 116 printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args); 117 } 118 119 private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) { 120 if (HotSpotPrintInlining.getValue(invoke.asNode().getOptions())) { 121 Util.printInlining(method, invoke.bci(), inliningDepth, success, msg, args); 122 } 123 } 124 125 public static void logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) { 126 logInliningDecision(info, inliningDepth, allowLogging, true, msg, args); 127 } 128 129 public static void logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) { 130 logInliningDecision(info, inliningDepth, true, false, msg, args); 131 } 132 133 public static void logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) { 134 if (allowLogging) { 135 printInlining(info, inliningDepth, success, msg, args); 136 DebugContext debug = info.graph().getDebug(); 137 if (shouldLogInliningDecision(debug)) { 138 logInliningDecision(debug, methodName(info), success, msg, args); 139 } 140 } 141 } 142 143 @SuppressWarnings("try") 144 public static void logInliningDecision(DebugContext debug, final String msg, final Object... args) { 145 try (DebugContext.Scope s = debug.scope(inliningDecisionsScopeString)) { 146 // Can't use log here since we are varargs 147 if (debug.isLogEnabled()) { 148 debug.logv(msg, args); 149 } 150 } 151 } 152 153 public static void logNotInlinedMethod(Invoke invoke, String msg) { 154 DebugContext debug = invoke.asNode().getDebug(); 155 if (shouldLogInliningDecision(debug)) { 156 String methodString = invoke.toString(); 157 if (invoke.callTarget() == null) { 158 methodString += " callTarget=null"; 159 } else { 160 String targetName = invoke.callTarget().targetName(); 161 if (!methodString.endsWith(targetName)) { 162 methodString += " " + targetName; 163 } 164 } 165 logInliningDecision(debug, methodString, false, msg, new Object[0]); 166 } 167 } 168 169 public static void logNotInlined(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { 170 logNotInlinedInvoke(invoke, inliningDepth, method, msg, new Object[0]); 171 } 172 173 public static void logNotInlinedInvoke(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) { 174 DebugContext debug = invoke.asNode().getDebug(); 175 printInlining(method, invoke, inliningDepth, false, msg, args); 176 if (shouldLogInliningDecision(debug)) { 177 String methodString = methodName(method, invoke); 178 logInliningDecision(debug, methodString, false, msg, args); 179 } 180 } 181 182 private static void logInliningDecision(DebugContext debug, final String methodString, final boolean success, final String msg, final Object... args) { 183 String inliningMsg = "inlining " + methodString + ": " + msg; 184 if (!success) { 185 inliningMsg = "not " + inliningMsg; 186 } 187 logInliningDecision(debug, inliningMsg, args); 188 } 189 190 @SuppressWarnings("try") 191 public static boolean shouldLogInliningDecision(DebugContext debug) { 192 try (DebugContext.Scope s = debug.scope(inliningDecisionsScopeString)) { 193 return debug.isLogEnabled(); 194 } 195 } 196 197 private static String methodName(ResolvedJavaMethod method, Invoke invoke) { 198 if (invoke != null && invoke.stateAfter() != null) { 199 return methodName(invoke.stateAfter(), invoke.bci()) + ": " + method.format("%H.%n(%p):%r") + " (" + method.getCodeSize() + " bytes)"; 200 } else { 201 return method.format("%H.%n(%p):%r") + " (" + method.getCodeSize() + " bytes)"; 202 } 203 } 204 205 private static String methodName(InlineInfo info) { 206 if (info == null) { 207 return "null"; 208 } else if (info.invoke() != null && info.invoke().stateAfter() != null) { 209 return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString(); 210 } else { 211 return info.toString(); 212 } 213 } 214 215 private static String methodName(FrameState frameState, int bci) { 216 StringBuilder sb = new StringBuilder(); 217 if (frameState.outerFrameState() != null) { 218 sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); 219 sb.append("->"); 220 } 221 ResolvedJavaMethod method = frameState.getMethod(); 222 sb.append(method != null ? method.format("%h.%n") : "?"); 223 sb.append("@").append(bci); 224 return sb.toString(); 225 } 226 227 public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) { 228 MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget(); 229 MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnStamp(), 230 oldCallTarget.getProfile())); 231 invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget); 232 } 233 234 public static PiNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { 235 return createAnchoredReceiver(graph, anchor, receiver, 236 exact ? StampFactory.objectNonNull(TypeReference.createExactTrusted(commonType)) : StampFactory.objectNonNull(TypeReference.createTrusted(graph.getAssumptions(), commonType))); 237 } 238 239 private static PiNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ValueNode receiver, Stamp stamp) { 240 // to avoid that floating reads on receiver fields float above the type check 241 return graph.unique(new PiNode(receiver, stamp, (ValueNode) anchor)); 242 } 243 244 /** 245 * @return null iff the check succeeds, otherwise a (non-null) descriptive message. 246 */ 247 public static String checkInvokeConditions(Invoke invoke) { 248 if (invoke.predecessor() == null || !invoke.asNode().isAlive()) { 249 return "the invoke is dead code"; 250 } 251 if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { 252 return "the invoke has already been lowered, or has been created as a low-level node"; 253 } 254 MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); 255 if (callTarget.targetMethod() == null) { 256 return "target method is null"; 257 } 258 assert invoke.stateAfter() != null : invoke; 259 if (!invoke.useForInlining()) { 260 return "the invoke is marked to be not used for inlining"; 261 } 262 ValueNode receiver = callTarget.receiver(); 263 if (receiver != null && receiver.isConstant() && receiver.isNullConstant()) { 264 return "receiver is null"; 265 } 266 return null; 267 } 268 269 /** 270 * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. 271 * 272 * @param invoke the invoke that will be replaced 273 * @param inlineGraph the graph that the invoke will be replaced with 274 * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, 275 * false if no such check is required 276 * @param inlineeMethod the actual method being inlined. Maybe be null for snippets. 277 */ 278 @SuppressWarnings("try") 279 public static UnmodifiableEconomicMap<Node, Node> inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck, ResolvedJavaMethod inlineeMethod) { 280 FixedNode invokeNode = invoke.asNode(); 281 StructuredGraph graph = invokeNode.graph(); 282 final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments(); 283 284 assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal(); 285 assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase"; 286 287 if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) { 288 nonNullReceiver(invoke); 289 } 290 291 ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count()); 292 ArrayList<ReturnNode> returnNodes = new ArrayList<>(4); 293 ArrayList<Invoke> partialIntrinsicExits = new ArrayList<>(); 294 UnwindNode unwindNode = null; 295 final StartNode entryPointNode = inlineGraph.start(); 296 FixedNode firstCFGNode = entryPointNode.next(); 297 if (firstCFGNode == null) { 298 throw new IllegalStateException("Inlined graph is in invalid state: " + inlineGraph); 299 } 300 for (Node node : inlineGraph.getNodes()) { 301 if (node == entryPointNode || (node == entryPointNode.stateAfter() && node.usages().count() == 1) || node instanceof ParameterNode) { 302 // Do nothing. 303 } else { 304 nodes.add(node); 305 if (node instanceof ReturnNode) { 306 returnNodes.add((ReturnNode) node); 307 } else if (node instanceof Invoke) { 308 Invoke invokeInInlineGraph = (Invoke) node; 309 if (invokeInInlineGraph.bci() == BytecodeFrame.UNKNOWN_BCI) { 310 ResolvedJavaMethod target1 = inlineeMethod; 311 ResolvedJavaMethod target2 = invokeInInlineGraph.callTarget().targetMethod(); 312 assert target1.equals(target2) : String.format("invoke in inlined method expected to be partial intrinsic exit (i.e., call to %s), not a call to %s", 313 target1.format("%H.%n(%p)"), target2.format("%H.%n(%p)")); 314 partialIntrinsicExits.add(invokeInInlineGraph); 315 } 316 } else if (node instanceof UnwindNode) { 317 assert unwindNode == null; 318 unwindNode = (UnwindNode) node; 319 } 320 } 321 } 322 323 final AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(invokeNode); 324 DuplicationReplacement localReplacement = new DuplicationReplacement() { 325 326 @Override 327 public Node replacement(Node node) { 328 if (node instanceof ParameterNode) { 329 return parameters.get(((ParameterNode) node).index()); 330 } else if (node == entryPointNode) { 331 return prevBegin; 332 } 333 return node; 334 } 335 }; 336 337 assert invokeNode.successors().first() != null : invoke; 338 assert invokeNode.predecessor() != null; 339 340 EconomicMap<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement); 341 342 FrameState stateAfter = invoke.stateAfter(); 343 assert stateAfter == null || stateAfter.isAlive(); 344 345 FrameState stateAtExceptionEdge = null; 346 if (invoke instanceof InvokeWithExceptionNode) { 347 InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); 348 if (unwindNode != null) { 349 ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge(); 350 stateAtExceptionEdge = obj.stateAfter(); 351 } 352 } 353 354 updateSourcePositions(invoke, inlineGraph, duplicates); 355 if (stateAfter != null) { 356 processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1); 357 int callerLockDepth = stateAfter.nestedLockDepth(); 358 if (callerLockDepth != 0) { 359 for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) { 360 MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original); 361 processMonitorId(invoke.stateAfter(), monitor); 362 } 363 } 364 } else { 365 assert checkContainsOnlyInvalidOrAfterFrameState(duplicates); 366 } 367 368 firstCFGNode = (FixedNode) duplicates.get(firstCFGNode); 369 for (int i = 0; i < returnNodes.size(); i++) { 370 returnNodes.set(i, (ReturnNode) duplicates.get(returnNodes.get(i))); 371 } 372 for (Invoke exit : partialIntrinsicExits) { 373 // A partial intrinsic exit must be replaced with a call to 374 // the intrinsified method. 375 Invoke dup = (Invoke) duplicates.get(exit.asNode()); 376 if (dup instanceof InvokeNode) { 377 InvokeNode repl = graph.add(new InvokeNode(invoke.callTarget(), invoke.bci())); 378 dup.intrinsify(repl.asNode()); 379 } else { 380 ((InvokeWithExceptionNode) dup).replaceWithNewBci(invoke.bci()); 381 } 382 } 383 if (unwindNode != null) { 384 unwindNode = (UnwindNode) duplicates.get(unwindNode); 385 } 386 387 finishInlining(invoke, graph, firstCFGNode, returnNodes, unwindNode, inlineGraph.getAssumptions(), inlineGraph); 388 GraphUtil.killCFG(invokeNode); 389 390 return duplicates; 391 } 392 393 /** 394 * Inline {@code inlineGraph} into the current replacing the node {@code Invoke} and return the 395 * set of nodes which should be canonicalized. The set should only contain nodes which modified 396 * by the inlining since the current graph and {@code inlineGraph} are expected to already be 397 * canonical. 398 * 399 * @param invoke 400 * @param inlineGraph 401 * @param receiverNullCheck 402 * @param inlineeMethod 403 * @return the set of nodes to canonicalize 404 */ 405 @SuppressWarnings("try") 406 public static EconomicSet<Node> inlineForCanonicalization(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck, ResolvedJavaMethod inlineeMethod) { 407 return inlineForCanonicalization(invoke, inlineGraph, receiverNullCheck, inlineeMethod, null); 408 } 409 410 @SuppressWarnings("try") 411 public static EconomicSet<Node> inlineForCanonicalization(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck, ResolvedJavaMethod inlineeMethod, 412 Consumer<UnmodifiableEconomicMap<Node, Node>> duplicatesConsumer) { 413 HashSetNodeEventListener listener = new HashSetNodeEventListener(); 414 /* 415 * This code relies on the fact that Graph.addDuplicates doesn't trigger the 416 * NodeEventListener to track only nodes which were modified into the process of inlining 417 * the graph into the current graph. 418 */ 419 try (NodeEventScope nes = invoke.asNode().graph().trackNodeEvents(listener)) { 420 UnmodifiableEconomicMap<Node, Node> duplicates = InliningUtil.inline(invoke, inlineGraph, receiverNullCheck, inlineeMethod); 421 if (duplicatesConsumer != null) { 422 duplicatesConsumer.accept(duplicates); 423 } 424 } 425 return listener.getNodes(); 426 } 427 428 private static ValueNode finishInlining(Invoke invoke, StructuredGraph graph, FixedNode firstNode, List<ReturnNode> returnNodes, UnwindNode unwindNode, Assumptions inlinedAssumptions, 429 StructuredGraph inlineGraph) { 430 FixedNode invokeNode = invoke.asNode(); 431 FrameState stateAfter = invoke.stateAfter(); 432 assert stateAfter == null || stateAfter.isAlive(); 433 434 invokeNode.replaceAtPredecessor(firstNode); 435 436 if (invoke instanceof InvokeWithExceptionNode) { 437 InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); 438 if (unwindNode != null && unwindNode.isAlive()) { 439 assert unwindNode.predecessor() != null; 440 assert invokeWithException.exceptionEdge().successors().count() == 1; 441 ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge(); 442 obj.replaceAtUsages(unwindNode.exception()); 443 Node n = obj.next(); 444 obj.setNext(null); 445 unwindNode.replaceAndDelete(n); 446 447 obj.replaceAtPredecessor(null); 448 obj.safeDelete(); 449 } else { 450 invokeWithException.killExceptionEdge(); 451 } 452 453 // get rid of memory kill 454 AbstractBeginNode begin = invokeWithException.next(); 455 if (begin instanceof KillingBeginNode) { 456 AbstractBeginNode newBegin = new BeginNode(); 457 graph.addAfterFixed(begin, graph.add(newBegin)); 458 begin.replaceAtUsages(newBegin); 459 graph.removeFixed(begin); 460 } 461 } else { 462 if (unwindNode != null && unwindNode.isAlive()) { 463 DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); 464 unwindNode.replaceAndDelete(deoptimizeNode); 465 } 466 } 467 468 ValueNode returnValue; 469 if (!returnNodes.isEmpty()) { 470 FixedNode n = invoke.next(); 471 invoke.setNext(null); 472 if (returnNodes.size() == 1) { 473 ReturnNode returnNode = returnNodes.get(0); 474 returnValue = returnNode.result(); 475 invokeNode.replaceAtUsages(returnValue); 476 returnNode.replaceAndDelete(n); 477 } else { 478 MergeNode merge = graph.add(new MergeNode()); 479 merge.setStateAfter(stateAfter); 480 returnValue = mergeReturns(merge, returnNodes); 481 invokeNode.replaceAtUsages(returnValue); 482 if (merge.isPhiAtMerge(returnValue)) { 483 fixFrameStates(graph, merge, (PhiNode) returnValue); 484 } 485 merge.setNext(n); 486 } 487 } else { 488 returnValue = null; 489 invokeNode.replaceAtUsages(null); 490 GraphUtil.killCFG(invoke.next()); 491 } 492 493 // Copy assumptions from inlinee to caller 494 Assumptions assumptions = graph.getAssumptions(); 495 if (assumptions != null) { 496 if (inlinedAssumptions != null) { 497 assumptions.record(inlinedAssumptions); 498 } 499 } else { 500 assert inlinedAssumptions == null : String.format("cannot inline graph (%s) which makes assumptions into a graph (%s) that doesn't", inlineGraph, graph); 501 } 502 503 // Copy inlined methods from inlinee to caller 504 graph.updateMethods(inlineGraph); 505 506 // Update the set of accessed fields 507 if (GraalOptions.GeneratePIC.getValue(graph.getOptions())) { 508 graph.updateFields(inlineGraph); 509 } 510 511 if (inlineGraph.hasUnsafeAccess()) { 512 graph.markUnsafeAccess(); 513 } 514 assert inlineGraph.getSpeculationLog() == null || inlineGraph.getSpeculationLog() == graph.getSpeculationLog() : "Only the root graph should have a speculation log"; 515 516 return returnValue; 517 } 518 519 private static void fixFrameStates(StructuredGraph graph, MergeNode originalMerge, PhiNode returnPhi) { 520 // It is possible that some of the frame states that came from AFTER_BCI reference a Phi 521 // node that was created to merge multiple returns. This can create cycles 522 // (see GR-3949 and GR-3957). 523 // To detect this, we follow the control paths starting from the merge node, 524 // split the Phi node inputs at merges and assign the proper input to each frame state. 525 NodeMap<Node> seen = new NodeMap<>(graph); 526 ArrayDeque<Node> workList = new ArrayDeque<>(); 527 ArrayDeque<ValueNode> valueList = new ArrayDeque<>(); 528 workList.push(originalMerge); 529 valueList.push(returnPhi); 530 while (!workList.isEmpty()) { 531 Node current = workList.pop(); 532 ValueNode currentValue = valueList.pop(); 533 if (seen.containsKey(current)) { 534 continue; 535 } 536 seen.put(current, current); 537 if (current instanceof StateSplit && current != originalMerge) { 538 StateSplit stateSplit = (StateSplit) current; 539 FrameState state = stateSplit.stateAfter(); 540 if (state != null && state.values().contains(returnPhi)) { 541 int index = 0; 542 FrameState duplicate = state.duplicate(); 543 for (ValueNode value : state.values()) { 544 if (value == returnPhi) { 545 duplicate.values().set(index, currentValue); 546 } 547 index++; 548 } 549 stateSplit.setStateAfter(duplicate); 550 GraphUtil.tryKillUnused(state); 551 } 552 } 553 if (current instanceof AbstractMergeNode) { 554 AbstractMergeNode currentMerge = (AbstractMergeNode) current; 555 for (EndNode pred : currentMerge.cfgPredecessors()) { 556 ValueNode newValue = currentValue; 557 if (currentMerge.isPhiAtMerge(currentValue)) { 558 PhiNode currentPhi = (PhiNode) currentValue; 559 newValue = currentPhi.valueAt(pred); 560 } 561 workList.push(pred); 562 valueList.push(newValue); 563 } 564 } else if (current.predecessor() != null) { 565 workList.push(current.predecessor()); 566 valueList.push(currentValue); 567 } 568 } 569 } 570 571 @SuppressWarnings("try") 572 private static void updateSourcePositions(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap<Node, Node> duplicates) { 573 if (inlineGraph.mayHaveNodeSourcePosition() && invoke.stateAfter() != null) { 574 if (invoke.asNode().getNodeSourcePosition() == null) { 575 // Temporarily ignore the assert below. 576 return; 577 } 578 579 JavaConstant constantReceiver = invoke.getInvokeKind().hasReceiver() ? invoke.getReceiver().asJavaConstant() : null; 580 NodeSourcePosition invokePos = invoke.asNode().getNodeSourcePosition(); 581 assert invokePos != null : "missing source information"; 582 583 EconomicMap<NodeSourcePosition, NodeSourcePosition> posMap = EconomicMap.create(Equivalence.DEFAULT); 584 UnmodifiableMapCursor<Node, Node> cursor = duplicates.getEntries(); 585 while (cursor.advance()) { 586 NodeSourcePosition pos = cursor.getKey().getNodeSourcePosition(); 587 if (pos != null) { 588 NodeSourcePosition callerPos = pos.addCaller(constantReceiver, invokePos); 589 if (!posMap.containsKey(callerPos)) { 590 posMap.put(callerPos, callerPos); 591 } 592 cursor.getValue().setNodeSourcePosition(posMap.get(callerPos)); 593 } 594 } 595 } 596 } 597 598 public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) { 599 if (stateAfter != null) { 600 int callerLockDepth = stateAfter.nestedLockDepth(); 601 monitorIdNode.setLockDepth(monitorIdNode.getLockDepth() + callerLockDepth); 602 } 603 } 604 605 protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, EconomicMap<Node, Node> duplicates, FrameState stateAtExceptionEdge, 606 boolean alwaysDuplicateStateAfter) { 607 FrameState stateAtReturn = invoke.stateAfter(); 608 FrameState outerFrameState = null; 609 JavaKind invokeReturnKind = invoke.asNode().getStackKind(); 610 EconomicMap<Node, Node> replacements = EconomicMap.create(); 611 for (FrameState original : inlineGraph.getNodes(FrameState.TYPE)) { 612 FrameState frameState = (FrameState) duplicates.get(original); 613 if (frameState != null && frameState.isAlive()) { 614 if (outerFrameState == null) { 615 outerFrameState = stateAtReturn.duplicateModifiedDuringCall(invoke.bci(), invokeReturnKind); 616 } 617 processFrameState(frameState, invoke, replacements, inlineGraph.method(), stateAtExceptionEdge, outerFrameState, alwaysDuplicateStateAfter, invoke.callTarget().targetMethod(), 618 invoke.callTarget().arguments()); 619 } 620 } 621 // If processing the frame states replaced any nodes, update the duplicates map. 622 duplicates.replaceAll((key, value) -> replacements.containsKey(value) ? replacements.get(value) : value); 623 } 624 625 public static FrameState processFrameState(FrameState frameState, Invoke invoke, EconomicMap<Node, Node> replacements, ResolvedJavaMethod inlinedMethod, FrameState stateAtExceptionEdge, 626 FrameState outerFrameState, 627 boolean alwaysDuplicateStateAfter, ResolvedJavaMethod invokeTargetMethod, List<ValueNode> invokeArgsList) { 628 assert outerFrameState == null || !outerFrameState.isDeleted() : outerFrameState; 629 final FrameState stateAtReturn = invoke.stateAfter(); 630 JavaKind invokeReturnKind = invoke.asNode().getStackKind(); 631 632 if (frameState.bci == BytecodeFrame.AFTER_BCI) { 633 return handleAfterBciFrameState(frameState, invoke, alwaysDuplicateStateAfter); 634 } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) { 635 // pop exception object from invoke's stateAfter and replace with this frameState's 636 // exception object (top of stack) 637 FrameState stateAfterException = stateAtExceptionEdge; 638 if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) { 639 stateAfterException = stateAtExceptionEdge.duplicateModified(JavaKind.Object, JavaKind.Object, frameState.stackAt(0)); 640 } 641 frameState.replaceAndDelete(stateAfterException); 642 return stateAfterException; 643 } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) { 644 handleMissingAfterExceptionFrameState(frameState, invoke, replacements, alwaysDuplicateStateAfter); 645 return frameState; 646 } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) { 647 // This is an intrinsic. Deoptimizing within an intrinsic 648 // must re-execute the intrinsified invocation 649 assert frameState.outerFrameState() == null; 650 ValueNode[] invokeArgs = invokeArgsList.isEmpty() ? NO_ARGS : invokeArgsList.toArray(new ValueNode[invokeArgsList.size()]); 651 FrameState stateBeforeCall = stateAtReturn.duplicateModifiedBeforeCall(invoke.bci(), invokeReturnKind, invokeTargetMethod.getSignature().toParameterKinds(!invokeTargetMethod.isStatic()), 652 invokeArgs); 653 frameState.replaceAndDelete(stateBeforeCall); 654 return stateBeforeCall; 655 } else { 656 // only handle the outermost frame states 657 if (frameState.outerFrameState() == null) { 658 assert checkInlineeFrameState(invoke, inlinedMethod, frameState); 659 frameState.setOuterFrameState(outerFrameState); 660 } 661 return frameState; 662 } 663 } 664 665 private static FrameState handleAfterBciFrameState(FrameState frameState, Invoke invoke, boolean alwaysDuplicateStateAfter) { 666 FrameState stateAtReturn = invoke.stateAfter(); 667 JavaKind invokeReturnKind = invoke.asNode().getStackKind(); 668 FrameState stateAfterReturn = stateAtReturn; 669 if (frameState.getCode() == null) { 670 // This is a frame state for a side effect within an intrinsic 671 // that was parsed for post-parse intrinsification 672 for (Node usage : frameState.usages()) { 673 if (usage instanceof ForeignCallNode) { 674 // A foreign call inside an intrinsic needs to have 675 // the BCI of the invoke being intrinsified 676 ForeignCallNode foreign = (ForeignCallNode) usage; 677 foreign.setBci(invoke.bci()); 678 } 679 } 680 } 681 682 // pop return kind from invoke's stateAfter and replace with this frameState's return 683 // value (top of stack) 684 assert !frameState.rethrowException() : frameState; 685 if (frameState.stackSize() > 0 && (alwaysDuplicateStateAfter || stateAfterReturn.stackAt(0) != frameState.stackAt(0))) { 686 // A non-void return value. 687 stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, invokeReturnKind, frameState.stackAt(0)); 688 } else { 689 // A void return value. 690 stateAfterReturn = stateAtReturn.duplicate(); 691 } 692 assert stateAfterReturn.bci != BytecodeFrame.UNKNOWN_BCI; 693 694 // Return value does no longer need to be limited by the monitor exit. 695 for (MonitorExitNode n : frameState.usages().filter(MonitorExitNode.class)) { 696 n.clearEscapedReturnValue(); 697 } 698 699 frameState.replaceAndDelete(stateAfterReturn); 700 return stateAfterReturn; 701 } 702 703 static boolean checkInlineeFrameState(Invoke invoke, ResolvedJavaMethod inlinedMethod, FrameState frameState) { 704 assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI : frameState; 705 assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState; 706 assert frameState.bci != BytecodeFrame.UNKNOWN_BCI : frameState; 707 assert frameState.bci != BytecodeFrame.UNWIND_BCI : frameState; 708 if (frameState.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI) { 709 ResolvedJavaMethod method = frameState.getMethod(); 710 if (method.equals(inlinedMethod)) { 711 // Normal inlining expects all outermost inlinee frame states to 712 // denote the inlinee method 713 } else if (method.equals(invoke.callTarget().targetMethod())) { 714 // This occurs when an intrinsic calls back to the original 715 // method to handle a slow path. During parsing of such a 716 // partial intrinsic, these calls are given frame states 717 // that exclude the outer frame state denoting a position 718 // in the intrinsic code. 719 assert inlinedMethod.getAnnotation( 720 MethodSubstitution.class) != null : "expected an intrinsic when inlinee frame state matches method of call target but does not match the method of the inlinee graph: " + 721 frameState; 722 } else if (method.getName().equals(inlinedMethod.getName())) { 723 // This can happen for method substitutions. 724 } else { 725 throw new AssertionError(String.format("inlinedMethod=%s frameState.method=%s frameState=%s invoke.method=%s", inlinedMethod, method, frameState, 726 invoke.callTarget().targetMethod())); 727 } 728 } 729 return true; 730 } 731 732 private static final ValueNode[] NO_ARGS = {}; 733 734 private static boolean isStateAfterException(FrameState frameState) { 735 return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.getMethod().isSynchronized()); 736 } 737 738 public static FrameState handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState, Invoke invoke, EconomicMap<Node, Node> replacements, boolean alwaysDuplicateStateAfter) { 739 Graph graph = nonReplaceableFrameState.graph(); 740 NodeWorkList workList = graph.createNodeWorkList(); 741 workList.add(nonReplaceableFrameState); 742 for (Node node : workList) { 743 FrameState fs = (FrameState) node; 744 for (Node usage : fs.usages().snapshot()) { 745 if (!usage.isAlive()) { 746 continue; 747 } 748 if (usage instanceof FrameState) { 749 workList.add(usage); 750 } else { 751 StateSplit stateSplit = (StateSplit) usage; 752 FixedNode fixedStateSplit = stateSplit.asNode(); 753 if (fixedStateSplit instanceof AbstractMergeNode) { 754 AbstractMergeNode merge = (AbstractMergeNode) fixedStateSplit; 755 while (merge.isAlive()) { 756 AbstractEndNode end = merge.forwardEnds().first(); 757 DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); 758 end.replaceAtPredecessor(deoptimizeNode); 759 GraphUtil.killCFG(end); 760 } 761 } else if (fixedStateSplit instanceof ExceptionObjectNode) { 762 // The target invoke does not have an exception edge. This means that the 763 // bytecode parser made the wrong assumption of making an 764 // InvokeWithExceptionNode for the partial intrinsic exit. We therefore 765 // replace the InvokeWithExceptionNode with a normal 766 // InvokeNode -- the deoptimization occurs when the invoke throws. 767 InvokeWithExceptionNode oldInvoke = (InvokeWithExceptionNode) fixedStateSplit.predecessor(); 768 FrameState oldFrameState = oldInvoke.stateAfter(); 769 InvokeNode newInvoke = oldInvoke.replaceWithInvoke(); 770 newInvoke.setStateAfter(oldFrameState.duplicate()); 771 if (replacements != null) { 772 replacements.put(oldInvoke, newInvoke); 773 } 774 handleAfterBciFrameState(newInvoke.stateAfter(), invoke, alwaysDuplicateStateAfter); 775 } else { 776 FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); 777 if (fixedStateSplit instanceof AbstractBeginNode) { 778 deoptimizeNode = BeginNode.begin(deoptimizeNode); 779 } 780 fixedStateSplit.replaceAtPredecessor(deoptimizeNode); 781 GraphUtil.killCFG(fixedStateSplit); 782 } 783 } 784 } 785 } 786 return nonReplaceableFrameState; 787 } 788 789 /** 790 * Ensure that all states are either {@link BytecodeFrame#INVALID_FRAMESTATE_BCI} or one of 791 * {@link BytecodeFrame#AFTER_BCI} or {@link BytecodeFrame#BEFORE_BCI}. Mixing of before and 792 * after isn't allowed. 793 */ 794 private static boolean checkContainsOnlyInvalidOrAfterFrameState(UnmodifiableEconomicMap<Node, Node> duplicates) { 795 int okBci = BytecodeFrame.INVALID_FRAMESTATE_BCI; 796 for (Node node : duplicates.getValues()) { 797 if (node instanceof FrameState) { 798 FrameState frameState = (FrameState) node; 799 if (frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { 800 continue; 801 } 802 if (frameState.bci == BytecodeFrame.AFTER_BCI || frameState.bci == BytecodeFrame.BEFORE_BCI) { 803 if (okBci == BytecodeFrame.INVALID_FRAMESTATE_BCI) { 804 okBci = frameState.bci; 805 } else { 806 assert okBci == frameState.bci : node.toString(Verbosity.Debugger); 807 } 808 } else { 809 assert false : node.toString(Verbosity.Debugger); 810 } 811 } 812 } 813 return true; 814 } 815 816 /** 817 * Gets the receiver for an invoke, adding a guard if necessary to ensure it is non-null, and 818 * ensuring that the resulting type is compatible with the method being invoked. 819 */ 820 public static ValueNode nonNullReceiver(Invoke invoke) { 821 MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); 822 assert !callTarget.isStatic() : callTarget.targetMethod(); 823 StructuredGraph graph = callTarget.graph(); 824 ValueNode oldReceiver = callTarget.arguments().get(0); 825 ValueNode newReceiver = oldReceiver; 826 if (newReceiver.getStackKind() == JavaKind.Object) { 827 828 if (invoke.getInvokeKind() == InvokeKind.Special) { 829 Stamp paramStamp = newReceiver.stamp(); 830 Stamp stamp = paramStamp.join(StampFactory.object(TypeReference.create(graph.getAssumptions(), callTarget.targetMethod().getDeclaringClass()))); 831 if (!stamp.equals(paramStamp)) { 832 // The verifier and previous optimizations guarantee unconditionally that the 833 // receiver is at least of the type of the method holder for a special invoke. 834 newReceiver = graph.unique(new PiNode(newReceiver, stamp)); 835 } 836 } 837 838 if (!StampTool.isPointerNonNull(newReceiver)) { 839 LogicNode condition = graph.unique(IsNullNode.create(newReceiver)); 840 FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(condition, NullCheckException, InvalidateReprofile, true)); 841 PiNode nonNullReceiver = graph.unique(new PiNode(newReceiver, StampFactory.objectNonNull(), fixedGuard)); 842 graph.addBeforeFixed(invoke.asNode(), fixedGuard); 843 newReceiver = nonNullReceiver; 844 } 845 } 846 847 if (newReceiver != oldReceiver) { 848 callTarget.replaceFirstInput(oldReceiver, newReceiver); 849 } 850 return newReceiver; 851 } 852 853 public static boolean canIntrinsify(Replacements replacements, ResolvedJavaMethod target, int invokeBci) { 854 return replacements.hasSubstitution(target, invokeBci); 855 } 856 857 public static StructuredGraph getIntrinsicGraph(Replacements replacements, ResolvedJavaMethod target, int invokeBci) { 858 return replacements.getSubstitution(target, invokeBci); 859 } 860 861 public static FixedWithNextNode inlineMacroNode(Invoke invoke, ResolvedJavaMethod concrete, Class<? extends FixedWithNextNode> macroNodeClass) throws GraalError { 862 StructuredGraph graph = invoke.asNode().graph(); 863 if (!concrete.equals(((MethodCallTargetNode) invoke.callTarget()).targetMethod())) { 864 assert ((MethodCallTargetNode) invoke.callTarget()).invokeKind().hasReceiver(); 865 InliningUtil.replaceInvokeCallTarget(invoke, graph, InvokeKind.Special, concrete); 866 } 867 868 FixedWithNextNode macroNode = createMacroNodeInstance(macroNodeClass, invoke); 869 870 CallTargetNode callTarget = invoke.callTarget(); 871 if (invoke instanceof InvokeNode) { 872 graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode)); 873 } else { 874 InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; 875 invokeWithException.killExceptionEdge(); 876 graph.replaceSplitWithFixed(invokeWithException, graph.add(macroNode), invokeWithException.next()); 877 } 878 GraphUtil.killWithUnusedFloatingInputs(callTarget); 879 return macroNode; 880 } 881 882 private static FixedWithNextNode createMacroNodeInstance(Class<? extends FixedWithNextNode> macroNodeClass, Invoke invoke) throws GraalError { 883 try { 884 Constructor<?> cons = macroNodeClass.getDeclaredConstructor(Invoke.class); 885 return (FixedWithNextNode) cons.newInstance(invoke); 886 } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { 887 throw new GraalGraphError(e).addContext(invoke.asNode()).addContext("macroSubstitution", macroNodeClass); 888 } 889 } 890 891 /** 892 * This method exclude InstrumentationNode from inlining heuristics. 893 */ 894 public static int getNodeCount(StructuredGraph graph) { 895 return graph.getNodeCount(); 896 } 897 898 }