1 /*
2  * Copyright (c) 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 
24 
25 package org.graalvm.compiler.replacements;
26 
27 import static org.graalvm.compiler.core.common.GraalOptions.MaximumRecursiveInlining;
28 
29 import org.graalvm.compiler.core.common.type.StampPair;
30 import org.graalvm.compiler.graph.NodeInputList;
31 import org.graalvm.compiler.nodes.CallTargetNode;
32 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
33 import org.graalvm.compiler.nodes.InvokeNode;
34 import org.graalvm.compiler.nodes.ValueNode;
35 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
36 import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin;
37 import org.graalvm.compiler.replacements.nodes.MethodHandleNode;
38 
39 import jdk.vm.ci.meta.JavaKind;
40 import jdk.vm.ci.meta.MethodHandleAccessProvider;
41 import jdk.vm.ci.meta.MethodHandleAccessProvider.IntrinsicMethod;
42 import jdk.vm.ci.meta.ResolvedJavaMethod;
43 
44 public class MethodHandlePlugin implements NodePlugin {
45     private final MethodHandleAccessProvider methodHandleAccess;
46     private final boolean safeForDeoptimization;
47 
MethodHandlePlugin(MethodHandleAccessProvider methodHandleAccess, boolean safeForDeoptimization)48     public MethodHandlePlugin(MethodHandleAccessProvider methodHandleAccess, boolean safeForDeoptimization) {
49         this.methodHandleAccess = methodHandleAccess;
50         this.safeForDeoptimization = safeForDeoptimization;
51     }
52 
countRecursiveInlining(GraphBuilderContext b, ResolvedJavaMethod method)53     private static int countRecursiveInlining(GraphBuilderContext b, ResolvedJavaMethod method) {
54         int count = 0;
55         for (GraphBuilderContext c = b.getParent(); c != null; c = c.getParent()) {
56             if (method.equals(c.getMethod())) {
57                 count++;
58             }
59         }
60         return count;
61     }
62 
63     @Override
handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args)64     public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
65         IntrinsicMethod intrinsicMethod = methodHandleAccess.lookupMethodHandleIntrinsic(method);
66         if (intrinsicMethod != null) {
67             InvokeKind invokeKind = b.getInvokeKind();
68             if (invokeKind != InvokeKind.Static) {
69                 args[0] = b.nullCheckedValue(args[0]);
70             }
71             StampPair invokeReturnStamp = b.getInvokeReturnStamp(b.getAssumptions());
72             MethodHandleNode.GraphAdder adder = new MethodHandleNode.GraphAdder(b.getGraph()) {
73                 @Override
74                 public <T extends ValueNode> T add(T node) {
75                     return b.add(node);
76                 }
77             };
78             InvokeNode invoke = MethodHandleNode.tryResolveTargetInvoke(adder, methodHandleAccess, intrinsicMethod, method, b.bci(), invokeReturnStamp, args);
79             if (invoke == null) {
80                 MethodHandleNode methodHandleNode = new MethodHandleNode(intrinsicMethod, invokeKind, method, b.bci(), invokeReturnStamp, args);
81                 if (invokeReturnStamp.getTrustedStamp().getStackKind() == JavaKind.Void) {
82                     b.add(methodHandleNode);
83                 } else {
84                     b.addPush(invokeReturnStamp.getTrustedStamp().getStackKind(), methodHandleNode);
85                 }
86             } else {
87                 CallTargetNode callTarget = invoke.callTarget();
88                 NodeInputList<ValueNode> argumentsList = callTarget.arguments();
89                 for (int i = 0; i < argumentsList.size(); ++i) {
90                     argumentsList.initialize(i, b.append(argumentsList.get(i)));
91                 }
92 
93                 boolean inlineEverything = false;
94                 if (safeForDeoptimization) {
95                     // If a MemberName suffix argument is dropped, the replaced call cannot
96                     // deoptimized since the necessary frame state cannot be reconstructed.
97                     // As such, it needs to recursively inline everything.
98                     inlineEverything = args.length != argumentsList.size();
99                 }
100                 ResolvedJavaMethod targetMethod = callTarget.targetMethod();
101                 if (inlineEverything && !targetMethod.hasBytecodes()) {
102                     // we need to force-inline but we can not, leave the invoke as-is
103                     return false;
104                 }
105 
106                 int recursionDepth = countRecursiveInlining(b, targetMethod);
107                 int maxRecursionDepth = MaximumRecursiveInlining.getValue(b.getOptions());
108                 if (recursionDepth > maxRecursionDepth) {
109                     return false;
110                 }
111 
112                 b.handleReplacedInvoke(invoke.getInvokeKind(), targetMethod, argumentsList.toArray(new ValueNode[argumentsList.size()]), inlineEverything);
113             }
114             return true;
115         }
116         return false;
117     }
118 }
119