001/*
002 * Copyright (c) 2012, 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;
024
025import static com.oracle.graal.compiler.common.GraalOptions.*;
026import static jdk.internal.jvmci.meta.DeoptimizationAction.*;
027import static jdk.internal.jvmci.meta.DeoptimizationReason.*;
028
029import java.lang.reflect.*;
030import java.util.*;
031
032import jdk.internal.jvmci.code.*;
033import jdk.internal.jvmci.common.*;
034import com.oracle.graal.debug.*;
035import com.oracle.graal.debug.Debug.Scope;
036
037import jdk.internal.jvmci.meta.*;
038
039import com.oracle.graal.api.replacements.*;
040import com.oracle.graal.compiler.common.type.*;
041import com.oracle.graal.graph.*;
042import com.oracle.graal.graph.Graph.DuplicationReplacement;
043import com.oracle.graal.nodeinfo.*;
044import com.oracle.graal.nodes.*;
045import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
046import com.oracle.graal.nodes.calc.*;
047import com.oracle.graal.nodes.extended.*;
048import com.oracle.graal.nodes.java.*;
049import com.oracle.graal.nodes.spi.*;
050import com.oracle.graal.nodes.type.*;
051import com.oracle.graal.nodes.util.*;
052import com.oracle.graal.phases.common.inlining.info.*;
053
054public class InliningUtil {
055
056    private static final String inliningDecisionsScopeString = "InliningDecisions";
057
058    /**
059     * Print a HotSpot-style inlining message to the console.
060     */
061    private static void printInlining(final InlineInfo info, final int inliningDepth, final boolean success, final String msg, final Object... args) {
062        printInlining(info.methodAt(0), info.invoke(), inliningDepth, success, msg, args);
063    }
064
065    /**
066     * Print a HotSpot-style inlining message to the console.
067     */
068    private static void printInlining(final ResolvedJavaMethod method, final Invoke invoke, final int inliningDepth, final boolean success, final String msg, final Object... args) {
069        if (HotSpotPrintInlining.getValue()) {
070            // 1234567
071            TTY.print("        ");     // print timestamp
072            // 1234
073            TTY.print("     ");        // print compilation number
074            // % s ! b n
075            TTY.print("%c%c%c%c%c ", ' ', method.isSynchronized() ? 's' : ' ', ' ', ' ', method.isNative() ? 'n' : ' ');
076            TTY.print("     ");        // more indent
077            TTY.print("    ");         // initial inlining indent
078            for (int i = 0; i < inliningDepth; i++) {
079                TTY.print("  ");
080            }
081            TTY.println(String.format("@ %d  %s   %s%s", invoke.bci(), methodName(method, null), success ? "" : "not inlining ", String.format(msg, args)));
082        }
083    }
084
085    public static void logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) {
086        logInliningDecision(info, inliningDepth, allowLogging, true, msg, args);
087    }
088
089    public static void logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) {
090        logInliningDecision(info, inliningDepth, true, false, msg, args);
091    }
092
093    public static void logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) {
094        if (allowLogging) {
095            printInlining(info, inliningDepth, success, msg, args);
096            if (shouldLogInliningDecision()) {
097                logInliningDecision(methodName(info), success, msg, args);
098            }
099        }
100    }
101
102    public static void logInliningDecision(final String msg, final Object... args) {
103        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
104            // Can't use log here since we are varargs
105            if (Debug.isLogEnabled()) {
106                Debug.logv(msg, args);
107            }
108        }
109    }
110
111    public static void logNotInlinedMethod(Invoke invoke, String msg) {
112        if (shouldLogInliningDecision()) {
113            String methodString = invoke.toString();
114            if (invoke.callTarget() == null) {
115                methodString += " callTarget=null";
116            } else {
117                String targetName = invoke.callTarget().targetName();
118                if (!methodString.endsWith(targetName)) {
119                    methodString += " " + targetName;
120                }
121            }
122            logInliningDecision(methodString, false, msg, new Object[0]);
123        }
124    }
125
126    public static void logNotInlined(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) {
127        logNotInlinedInvoke(invoke, inliningDepth, method, msg, new Object[0]);
128    }
129
130    public static void logNotInlinedInvoke(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) {
131        printInlining(method, invoke, inliningDepth, false, msg, args);
132        if (shouldLogInliningDecision()) {
133            String methodString = methodName(method, invoke);
134            logInliningDecision(methodString, false, msg, args);
135        }
136    }
137
138    private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) {
139        String inliningMsg = "inlining " + methodString + ": " + msg;
140        if (!success) {
141            inliningMsg = "not " + inliningMsg;
142        }
143        logInliningDecision(inliningMsg, args);
144    }
145
146    public static boolean shouldLogInliningDecision() {
147        try (Scope s = Debug.scope(inliningDecisionsScopeString)) {
148            return Debug.isLogEnabled();
149        }
150    }
151
152    private static String methodName(ResolvedJavaMethod method, Invoke invoke) {
153        if (invoke != null && invoke.stateAfter() != null) {
154            return methodName(invoke.stateAfter(), invoke.bci()) + ": " + method.format("%H.%n(%p):%r") + " (" + method.getCodeSize() + " bytes)";
155        } else {
156            return method.format("%H.%n(%p):%r") + " (" + method.getCodeSize() + " bytes)";
157        }
158    }
159
160    private static String methodName(InlineInfo info) {
161        if (info == null) {
162            return "null";
163        } else if (info.invoke() != null && info.invoke().stateAfter() != null) {
164            return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString();
165        } else {
166            return info.toString();
167        }
168    }
169
170    private static String methodName(FrameState frameState, int bci) {
171        StringBuilder sb = new StringBuilder();
172        if (frameState.outerFrameState() != null) {
173            sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci));
174            sb.append("->");
175        }
176        sb.append(frameState.method().format("%h.%n"));
177        sb.append("@").append(bci);
178        return sb.toString();
179    }
180
181    public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) {
182        MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget();
183        MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnType(),
184                        oldCallTarget.getProfile()));
185        invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget);
186    }
187
188    public static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) {
189        return createAnchoredReceiver(graph, anchor, receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredTrustedNonNull(commonType));
190    }
191
192    private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ValueNode receiver, Stamp stamp) {
193        // to avoid that floating reads on receiver fields float above the type check
194        return graph.unique(new GuardedValueNode(receiver, anchor, stamp));
195    }
196
197    /**
198     * @return null iff the check succeeds, otherwise a (non-null) descriptive message.
199     */
200    public static String checkInvokeConditions(Invoke invoke) {
201        if (invoke.predecessor() == null || !invoke.asNode().isAlive()) {
202            return "the invoke is dead code";
203        }
204        if (!(invoke.callTarget() instanceof MethodCallTargetNode)) {
205            return "the invoke has already been lowered, or has been created as a low-level node";
206        }
207        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
208        if (callTarget.targetMethod() == null) {
209            return "target method is null";
210        }
211        assert invoke.stateAfter() != null : invoke;
212        if (!invoke.useForInlining()) {
213            return "the invoke is marked to be not used for inlining";
214        }
215        ValueNode receiver = callTarget.receiver();
216        if (receiver != null && receiver.isConstant() && receiver.isNullConstant()) {
217            return "receiver is null";
218        }
219        return null;
220    }
221
222    /**
223     * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph.
224     *
225     * @param invoke the invoke that will be replaced
226     * @param inlineGraph the graph that the invoke will be replaced with
227     * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings,
228     *            false if no such check is required
229     * @param canonicalizedNodes if non-null then append to this list any nodes which should be
230     *            canonicalized after inlining
231     */
232    public static Map<Node, Node> inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck, List<Node> canonicalizedNodes) {
233        if (Fingerprint.ENABLED) {
234            Fingerprint.submit("inlining %s into %s: %s", formatGraph(inlineGraph), formatGraph(invoke.asNode().graph()), inlineGraph.getNodes().snapshot());
235        }
236        final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
237        FixedNode invokeNode = invoke.asNode();
238        StructuredGraph graph = invokeNode.graph();
239        assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
240        assert !invokeNode.graph().isAfterFloatingReadPhase() : "inline isn't handled correctly after floating reads phase";
241
242        if (receiverNullCheck && !((MethodCallTargetNode) invoke.callTarget()).isStatic()) {
243            nonNullReceiver(invoke);
244        }
245
246        ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
247        ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
248        UnwindNode unwindNode = null;
249        final StartNode entryPointNode = inlineGraph.start();
250        FixedNode firstCFGNode = entryPointNode.next();
251        if (firstCFGNode == null) {
252            throw new IllegalStateException("Inlined graph is in invalid state: " + inlineGraph);
253        }
254        for (Node node : inlineGraph.getNodes()) {
255            if (node == entryPointNode || (node == entryPointNode.stateAfter() && node.usages().count() == 1) || node instanceof ParameterNode) {
256                // Do nothing.
257            } else {
258                nodes.add(node);
259                if (node instanceof ReturnNode) {
260                    returnNodes.add((ReturnNode) node);
261                } else if (node instanceof UnwindNode) {
262                    assert unwindNode == null;
263                    unwindNode = (UnwindNode) node;
264                }
265            }
266        }
267
268        final AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(invokeNode);
269        DuplicationReplacement localReplacement = new DuplicationReplacement() {
270
271            public Node replacement(Node node) {
272                if (node instanceof ParameterNode) {
273                    return parameters.get(((ParameterNode) node).index());
274                } else if (node == entryPointNode) {
275                    return prevBegin;
276                }
277                return node;
278            }
279        };
280
281        assert invokeNode.successors().first() != null : invoke;
282        assert invokeNode.predecessor() != null;
283
284        Map<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
285
286        FrameState stateAfter = invoke.stateAfter();
287        assert stateAfter == null || stateAfter.isAlive();
288
289        FrameState stateAtExceptionEdge = null;
290        if (invoke instanceof InvokeWithExceptionNode) {
291            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
292            if (unwindNode != null) {
293                ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
294                stateAtExceptionEdge = obj.stateAfter();
295            }
296        }
297
298        processSimpleInfopoints(invoke, inlineGraph, duplicates);
299        if (stateAfter != null) {
300            processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1);
301            int callerLockDepth = stateAfter.nestedLockDepth();
302            if (callerLockDepth != 0) {
303                for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) {
304                    MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original);
305                    processMonitorId(invoke.stateAfter(), monitor);
306                }
307            }
308        } else {
309            assert checkContainsOnlyInvalidOrAfterFrameState(duplicates);
310        }
311
312        firstCFGNode = (FixedNode) duplicates.get(firstCFGNode);
313        for (int i = 0; i < returnNodes.size(); i++) {
314            returnNodes.set(i, (ReturnNode) duplicates.get(returnNodes.get(i)));
315        }
316        if (unwindNode != null) {
317            unwindNode = (UnwindNode) duplicates.get(unwindNode);
318        }
319
320        finishInlining(invoke, graph, firstCFGNode, returnNodes, unwindNode, inlineGraph.getAssumptions(), inlineGraph, canonicalizedNodes);
321
322        GraphUtil.killCFG(invokeNode);
323
324        return duplicates;
325    }
326
327    public static ValueNode finishInlining(Invoke invoke, StructuredGraph graph, FixedNode firstNode, List<ReturnNode> returnNodes, UnwindNode unwindNode, Assumptions inlinedAssumptions,
328                    StructuredGraph inlineGraph, List<Node> canonicalizedNodes) {
329        FixedNode invokeNode = invoke.asNode();
330        FrameState stateAfter = invoke.stateAfter();
331        assert stateAfter == null || stateAfter.isAlive();
332
333        invokeNode.replaceAtPredecessor(firstNode);
334
335        if (invoke instanceof InvokeWithExceptionNode) {
336            InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke);
337            if (unwindNode != null && unwindNode.isAlive()) {
338                assert unwindNode.predecessor() != null;
339                assert invokeWithException.exceptionEdge().successors().count() == 1;
340                ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
341                obj.replaceAtUsages(unwindNode.exception());
342                Node n = obj.next();
343                obj.setNext(null);
344                unwindNode.replaceAndDelete(n);
345
346                obj.replaceAtPredecessor(null);
347                obj.safeDelete();
348            } else {
349                invokeWithException.killExceptionEdge();
350            }
351
352            // get rid of memory kill
353            AbstractBeginNode begin = invokeWithException.next();
354            if (begin instanceof KillingBeginNode) {
355                AbstractBeginNode newBegin = new BeginNode();
356                graph.addAfterFixed(begin, graph.add(newBegin));
357                begin.replaceAtUsages(newBegin);
358                graph.removeFixed(begin);
359            }
360        } else {
361            if (unwindNode != null && unwindNode.isAlive()) {
362                DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
363                unwindNode.replaceAndDelete(deoptimizeNode);
364            }
365        }
366
367        ValueNode returnValue;
368        if (!returnNodes.isEmpty()) {
369            FixedNode n = invoke.next();
370            invoke.setNext(null);
371            if (returnNodes.size() == 1) {
372                ReturnNode returnNode = returnNodes.get(0);
373                returnValue = returnNode.result();
374                invokeNode.replaceAtUsages(returnValue);
375                returnNode.replaceAndDelete(n);
376            } else {
377                AbstractMergeNode merge = graph.add(new MergeNode());
378                merge.setStateAfter(stateAfter);
379                returnValue = mergeReturns(merge, returnNodes, canonicalizedNodes);
380                invokeNode.replaceAtUsages(returnValue);
381                merge.setNext(n);
382            }
383        } else {
384            returnValue = null;
385            invokeNode.replaceAtUsages(null);
386            GraphUtil.killCFG(invoke.next());
387        }
388
389        // Copy assumptions from inlinee to caller
390        Assumptions assumptions = graph.getAssumptions();
391        if (assumptions != null) {
392            if (inlinedAssumptions != null) {
393                assumptions.record(inlinedAssumptions);
394            }
395        } else {
396            assert inlinedAssumptions == null : "cannot inline graph which makes assumptions into a graph that doesn't";
397        }
398
399        // Copy inlined methods from inlinee to caller
400        graph.updateInlinedMethods(inlineGraph);
401        if (inlineGraph.hasUnsafeAccess()) {
402            graph.markUnsafeAccess();
403        }
404        assert inlineGraph.getSpeculationLog() == null : "Only the root graph should have a speculation log";
405
406        return returnValue;
407    }
408
409    private static String formatGraph(StructuredGraph graph) {
410        if (graph.method() == null) {
411            return graph.name;
412        }
413        return graph.method().format("%H.%n(%p)");
414    }
415
416    private static void processSimpleInfopoints(Invoke invoke, StructuredGraph inlineGraph, Map<Node, Node> duplicates) {
417        if (inlineGraph.getNodes(SimpleInfopointNode.TYPE).isEmpty()) {
418            return;
419        }
420        BytecodePosition pos = null;
421        for (SimpleInfopointNode original : inlineGraph.getNodes(SimpleInfopointNode.TYPE)) {
422            SimpleInfopointNode duplicate = (SimpleInfopointNode) duplicates.get(original);
423            pos = processSimpleInfopoint(invoke, duplicate, pos);
424        }
425    }
426
427    public static BytecodePosition processSimpleInfopoint(Invoke invoke, SimpleInfopointNode infopointNode, BytecodePosition incomingPos) {
428        BytecodePosition pos = processBytecodePosition(invoke, incomingPos);
429        infopointNode.addCaller(pos);
430        assert infopointNode.verify();
431        return pos;
432    }
433
434    public static BytecodePosition processBytecodePosition(Invoke invoke, BytecodePosition incomingPos) {
435        assert invoke.stateAfter() != null;
436        assert incomingPos == null || incomingPos.equals(InliningUtil.processBytecodePosition(invoke, null)) : incomingPos + " " + InliningUtil.processBytecodePosition(invoke, null);
437        return incomingPos != null ? incomingPos : new BytecodePosition(FrameState.toBytecodePosition(invoke.stateAfter().outerFrameState()), invoke.stateAfter().method(), invoke.bci());
438    }
439
440    public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) {
441        if (stateAfter != null) {
442            int callerLockDepth = stateAfter.nestedLockDepth();
443            monitorIdNode.setLockDepth(monitorIdNode.getLockDepth() + callerLockDepth);
444        }
445    }
446
447    protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map<Node, Node> duplicates, FrameState stateAtExceptionEdge, boolean alwaysDuplicateStateAfter) {
448        FrameState stateAtReturn = invoke.stateAfter();
449        FrameState outerFrameState = null;
450        Kind invokeReturnKind = invoke.asNode().getKind();
451        for (FrameState original : inlineGraph.getNodes(FrameState.TYPE)) {
452            FrameState frameState = (FrameState) duplicates.get(original);
453            if (frameState != null && frameState.isAlive()) {
454                if (outerFrameState == null) {
455                    outerFrameState = stateAtReturn.duplicateModifiedDuringCall(invoke.bci(), invokeReturnKind);
456                }
457                processFrameState(frameState, invoke, inlineGraph.method(), stateAtExceptionEdge, outerFrameState, alwaysDuplicateStateAfter);
458            }
459        }
460    }
461
462    public static FrameState processFrameState(FrameState frameState, Invoke invoke, ResolvedJavaMethod inlinedMethod, FrameState stateAtExceptionEdge, FrameState outerFrameState,
463                    boolean alwaysDuplicateStateAfter) {
464
465        FrameState stateAtReturn = invoke.stateAfter();
466        Kind invokeReturnKind = invoke.asNode().getKind();
467
468        if (frameState.bci == BytecodeFrame.AFTER_BCI) {
469            FrameState stateAfterReturn = stateAtReturn;
470            if (frameState.method() == null) {
471                // This is a frame state for a side effect within an intrinsic
472                // that was parsed for post-parse intrinsification
473                for (Node usage : frameState.usages()) {
474                    if (usage instanceof ForeignCallNode) {
475                        // A foreign call inside an intrinsic needs to have
476                        // the BCI of the invoke being intrinsified
477                        ForeignCallNode foreign = (ForeignCallNode) usage;
478                        foreign.setBci(invoke.bci());
479                    }
480                }
481            }
482
483            /*
484             * pop return kind from invoke's stateAfter and replace with this frameState's return
485             * value (top of stack)
486             */
487            if (frameState.stackSize() > 0 && (alwaysDuplicateStateAfter || stateAfterReturn.stackAt(0) != frameState.stackAt(0))) {
488                stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, invokeReturnKind, frameState.stackAt(0));
489            }
490
491            frameState.replaceAndDelete(stateAfterReturn);
492            return stateAfterReturn;
493        } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) {
494            /*
495             * pop exception object from invoke's stateAfter and replace with this frameState's
496             * exception object (top of stack)
497             */
498            FrameState stateAfterException = stateAtExceptionEdge;
499            if (frameState.stackSize() > 0 && stateAtExceptionEdge.stackAt(0) != frameState.stackAt(0)) {
500                stateAfterException = stateAtExceptionEdge.duplicateModified(Kind.Object, Kind.Object, frameState.stackAt(0));
501            }
502            frameState.replaceAndDelete(stateAfterException);
503            return stateAfterException;
504        } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) {
505            return handleMissingAfterExceptionFrameState(frameState);
506        } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) {
507            // This is an intrinsic. Deoptimizing within an intrinsic
508            // must re-execute the intrinsified invocation
509            assert frameState.outerFrameState() == null;
510            NodeInputList<ValueNode> invokeArgsList = invoke.callTarget().arguments();
511            ValueNode[] invokeArgs = invokeArgsList.isEmpty() ? NO_ARGS : invokeArgsList.toArray(new ValueNode[invokeArgsList.size()]);
512            ResolvedJavaMethod targetMethod = invoke.callTarget().targetMethod();
513            FrameState stateBeforeCall = stateAtReturn.duplicateModifiedBeforeCall(invoke.bci(), invokeReturnKind, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), invokeArgs);
514            frameState.replaceAndDelete(stateBeforeCall);
515            return stateBeforeCall;
516        } else {
517            // only handle the outermost frame states
518            if (frameState.outerFrameState() == null) {
519                assert checkInlineeFrameState(invoke, inlinedMethod, frameState);
520                frameState.setOuterFrameState(outerFrameState);
521            }
522            return frameState;
523        }
524    }
525
526    static boolean checkInlineeFrameState(Invoke invoke, ResolvedJavaMethod inlinedMethod, FrameState frameState) {
527        assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI : frameState;
528        assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState;
529        assert frameState.bci != BytecodeFrame.UNKNOWN_BCI : frameState;
530        assert frameState.bci != BytecodeFrame.UNWIND_BCI : frameState;
531        if (frameState.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI) {
532            if (frameState.method().equals(inlinedMethod)) {
533                // Normal inlining expects all outermost inlinee frame states to
534                // denote the inlinee method
535            } else if (frameState.method().equals(invoke.callTarget().targetMethod())) {
536                // This occurs when an intrinsic calls back to the original
537                // method to handle a slow path. During parsing of such a
538                // partial intrinsic, these calls are given frame states
539                // that exclude the outer frame state denoting a position
540                // in the intrinsic code.
541                assert inlinedMethod.getAnnotation(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: " +
542                                frameState;
543            } else if (frameState.method().getName().equals(inlinedMethod.getName())) {
544                // This can happen for method substitutions.
545            } else {
546                throw new AssertionError(String.format("inlinedMethod=%s frameState.method=%s frameState=%s invoke.method=%s", inlinedMethod, frameState.method(), frameState,
547                                invoke.callTarget().targetMethod()));
548            }
549        }
550        return true;
551    }
552
553    private static final ValueNode[] NO_ARGS = {};
554
555    private static boolean isStateAfterException(FrameState frameState) {
556        return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.method().isSynchronized());
557    }
558
559    protected static FrameState handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState) {
560        Graph graph = nonReplaceableFrameState.graph();
561        NodeWorkList workList = graph.createNodeWorkList();
562        workList.add(nonReplaceableFrameState);
563        for (Node node : workList) {
564            FrameState fs = (FrameState) node;
565            for (Node usage : fs.usages().snapshot()) {
566                if (!usage.isAlive()) {
567                    continue;
568                }
569                if (usage instanceof FrameState) {
570                    workList.add(usage);
571                } else {
572                    StateSplit stateSplit = (StateSplit) usage;
573                    FixedNode fixedStateSplit = stateSplit.asNode();
574                    if (fixedStateSplit instanceof AbstractMergeNode) {
575                        AbstractMergeNode merge = (AbstractMergeNode) fixedStateSplit;
576                        while (merge.isAlive()) {
577                            AbstractEndNode end = merge.forwardEnds().first();
578                            DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
579                            end.replaceAtPredecessor(deoptimizeNode);
580                            GraphUtil.killCFG(end);
581                        }
582                    } else {
583                        FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
584                        if (fixedStateSplit instanceof AbstractBeginNode) {
585                            deoptimizeNode = BeginNode.begin(deoptimizeNode);
586                        }
587                        fixedStateSplit.replaceAtPredecessor(deoptimizeNode);
588                        GraphUtil.killCFG(fixedStateSplit);
589                    }
590                }
591            }
592        }
593        return null;
594    }
595
596    public static ValueNode mergeReturns(AbstractMergeNode merge, List<? extends ReturnNode> returnNodes, List<Node> canonicalizedNodes) {
597        ValueNode singleReturnValue = null;
598        PhiNode returnValuePhi = null;
599        for (ReturnNode returnNode : returnNodes) {
600            ValueNode result = returnNode.result();
601            if (result != null) {
602                if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == result)) {
603                    /* Only one return value, so no need yet for a phi node. */
604                    singleReturnValue = result;
605
606                } else if (returnValuePhi == null) {
607                    /* Found a second return value, so create phi node. */
608                    returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp().unrestricted(), merge));
609                    if (canonicalizedNodes != null) {
610                        canonicalizedNodes.add(returnValuePhi);
611                    }
612                    for (int i = 0; i < merge.forwardEndCount(); i++) {
613                        returnValuePhi.addInput(singleReturnValue);
614                    }
615                    returnValuePhi.addInput(result);
616
617                } else {
618                    /* Multiple return values, just add to existing phi node. */
619                    returnValuePhi.addInput(result);
620                }
621            }
622
623            // create and wire up a new EndNode
624            EndNode endNode = merge.graph().add(new EndNode());
625            merge.addForwardEnd(endNode);
626            returnNode.replaceAndDelete(endNode);
627        }
628
629        if (returnValuePhi != null) {
630            assert returnValuePhi.verify();
631            returnValuePhi.inferStamp();
632            return returnValuePhi;
633        } else {
634            return singleReturnValue;
635        }
636    }
637
638    private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map<Node, Node> duplicates) {
639        for (Node node : duplicates.values()) {
640            if (node instanceof FrameState) {
641                FrameState frameState = (FrameState) node;
642                assert frameState.bci == BytecodeFrame.AFTER_BCI || frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI : node.toString(Verbosity.Debugger);
643            }
644        }
645        return true;
646    }
647
648    /**
649     * Gets the receiver for an invoke, adding a guard if necessary to ensure it is non-null, and
650     * ensuring that the resulting type is compatible with the method being invoked.
651     */
652    public static ValueNode nonNullReceiver(Invoke invoke) {
653        MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
654        assert !callTarget.isStatic() : callTarget.targetMethod();
655        StructuredGraph graph = callTarget.graph();
656        ValueNode firstParam = callTarget.arguments().get(0);
657        if (firstParam.getKind() == Kind.Object) {
658            Stamp paramStamp = firstParam.stamp();
659            Stamp stamp = paramStamp.join(StampFactory.declaredNonNull(callTarget.targetMethod().getDeclaringClass()));
660            if (!StampTool.isPointerNonNull(firstParam)) {
661                IsNullNode condition = graph.unique(new IsNullNode(firstParam));
662                FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(condition, NullCheckException, InvalidateReprofile, true));
663                PiNode nonNullReceiver = graph.unique(new PiNode(firstParam, stamp, fixedGuard));
664                graph.addBeforeFixed(invoke.asNode(), fixedGuard);
665                callTarget.replaceFirstInput(firstParam, nonNullReceiver);
666                return nonNullReceiver;
667            }
668            if (!stamp.equals(paramStamp)) {
669                PiNode cast = graph.unique(new PiNode(firstParam, stamp));
670                callTarget.replaceFirstInput(firstParam, cast);
671                return cast;
672            }
673        }
674        return firstParam;
675    }
676
677    public static boolean canIntrinsify(Replacements replacements, ResolvedJavaMethod target, int invokeBci) {
678        return replacements.hasSubstitution(target, false, invokeBci);
679    }
680
681    public static StructuredGraph getIntrinsicGraph(Replacements replacements, ResolvedJavaMethod target, int invokeBci) {
682        return replacements.getSubstitution(target, invokeBci);
683    }
684
685    public static FixedWithNextNode inlineMacroNode(Invoke invoke, ResolvedJavaMethod concrete, Class<? extends FixedWithNextNode> macroNodeClass) throws JVMCIError {
686        StructuredGraph graph = invoke.asNode().graph();
687        if (!concrete.equals(((MethodCallTargetNode) invoke.callTarget()).targetMethod())) {
688            assert ((MethodCallTargetNode) invoke.callTarget()).invokeKind().hasReceiver();
689            InliningUtil.replaceInvokeCallTarget(invoke, graph, InvokeKind.Special, concrete);
690        }
691
692        FixedWithNextNode macroNode = createMacroNodeInstance(macroNodeClass, invoke);
693
694        CallTargetNode callTarget = invoke.callTarget();
695        if (invoke instanceof InvokeNode) {
696            graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode));
697        } else {
698            InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke;
699            invokeWithException.killExceptionEdge();
700            graph.replaceSplitWithFixed(invokeWithException, graph.add(macroNode), invokeWithException.next());
701        }
702        GraphUtil.killWithUnusedFloatingInputs(callTarget);
703        return macroNode;
704    }
705
706    private static FixedWithNextNode createMacroNodeInstance(Class<? extends FixedWithNextNode> macroNodeClass, Invoke invoke) throws JVMCIError {
707        try {
708            Constructor<?> cons = macroNodeClass.getDeclaredConstructor(Invoke.class);
709            return (FixedWithNextNode) cons.newInstance(invoke);
710        } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
711            throw new GraalGraphJVMCIError(e).addContext(invoke.asNode()).addContext("macroSubstitution", macroNodeClass);
712        }
713    }
714}