1 /*
2 * Copyright (c) 2024, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package hat.callgraph;
26
27 import hat.dialect.DialectModule;
28 import jdk.incubator.code.dialect.core.CoreOp;
29 import jdk.incubator.code.dialect.java.MethodRef;
30 import optkl.OpHelper;
31 import optkl.util.Dag;
32 import optkl.util.carriers.FuncOpCarrier;
33
34 import java.lang.invoke.MethodHandles;
35 import java.lang.reflect.Method;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Optional;
40
41 public class MethodCallDag extends Dag<MethodCallDag.Call> {
42 public abstract static class Call implements FuncOpCarrier {
43 private final MethodRef methodRef;
44 private final Method method;
45 private CoreOp.FuncOp funcOp;
46
47 Call(MethodRef methodRef, Method method, CoreOp.FuncOp funcOp) {
48 this.methodRef = methodRef;
49 this.method = method;
50 this.funcOp = funcOp;
51 }
52
53 @Override
54 public int hashCode() {
55 return Objects.hash(methodRef, method); // We exclude the funcOp!
56 }
57
58 @Override
59 public boolean equals(Object o) {
60 return (this == o)
61 || (o instanceof Call that
62 && Objects.equals(methodRef, that.methodRef) && Objects.equals(method, that.method) // we exclude the funcOp
63 );
64 }
65
66 @Override
67 public CoreOp.FuncOp funcOp() {
68 return this.funcOp;
69 }
70
71 @Override
72 public void funcOp(CoreOp.FuncOp funcOp) {
73 this.funcOp = funcOp;
74 }
75
76 public Method method() {
77 return method;
78 }
79
80 public MethodRef methodRef() {
81 return this.methodRef;
82 }
83 }
84
85 public static class EntryMethodCall extends Call {
86 EntryMethodCall(Method method, CoreOp.FuncOp funcOp) {
87 super(null, method, funcOp); // we dont have a methodRef for the root
88 }
89 }
90
91 public static class OtherMethodCall extends Call {
92 OtherMethodCall(OpHelper.Invoke invoke) {
93 super(invoke.op().invokeReference(), invoke.resolveMethodOrThrow(), invoke.targetMethodModelOrNull()); // we dont have a methodRef for the root
94 }
95
96 OtherMethodCall(OpHelper.Invoke invoke, CoreOp.FuncOp funcOp) {
97 super(invoke.op().invokeReference(), invoke.resolveMethodOrThrow(), funcOp);
98 }
99 }
100
101
102 public final EntryMethodCall entryPoint;
103 public final CoreOp.FuncOp inlined;
104
105 // recursive
106 void addEdge(Call from, OpHelper.Invoke invoke) {
107 var to = new OtherMethodCall(invoke);
108 add(from, to, _ -> //only called if from->to is a new 'edge'
109 OpHelper.Invoke.stream(invoke.lookup(), to.funcOp())
110 .filter((inv) -> inv.targetMethodModelOrNull() != null)
111 .forEach(i -> addEdge(to, i))
112 );
113 }
114
115 void addEdge(Call from, OpHelper.Invoke invoke, Map<MethodRef, CoreOp.FuncOp> mapFunctions) {
116 MethodRef methodRef = invoke.op().invokeReference();
117 CoreOp.FuncOp funcOp = mapFunctions.get(methodRef);
118 var to = new OtherMethodCall(invoke, funcOp);
119 add(from, to, _ ->
120 OpHelper.Invoke.stream(invoke.lookup(), to.funcOp())
121 .filter((inv) -> inv.targetMethodModelOrNull() != null)
122 .forEach(i -> addEdge(to, i, mapFunctions))
123 );
124 }
125
126 private Map<MethodRef, CoreOp.FuncOp> ofDialect(DialectModule dialectModule) {
127 if (dialectModule != null) {
128 return dialectModule.functionsByMethod();
129 } else {
130 // return an empty hashmap
131 return new HashMap<>();
132 }
133 }
134
135 MethodCallDag(MethodHandles.Lookup lookup, Method method, CoreOp.FuncOp entry, CoreOp.FuncOp inlined) {
136 this.inlined = inlined;
137 this.entryPoint = new EntryMethodCall(method, entry);
138 add(entryPoint);
139 OpHelper.Invoke.stream(lookup, entry)
140 .filter((invoke) -> invoke.targetMethodModelOrNull() != null)
141 .forEach(invoke -> addEdge(entryPoint, invoke));
142 closeRanks();
143 }
144
145 MethodCallDag(MethodHandles.Lookup lookup,
146 Method method,
147 CoreOp.FuncOp entry,
148 CoreOp.FuncOp inlined,
149 DialectModule dialectModule) {
150
151 this.inlined = inlined;
152 this.entryPoint = new EntryMethodCall(method, entry);
153
154 // Add the main function to the CallDag
155 add(entryPoint);
156 Map<MethodRef, CoreOp.FuncOp> mapFunctions = ofDialect(dialectModule);
157 OpHelper.Invoke.stream(lookup, entry)
158 .filter((invoke) -> invoke.targetMethodModelOrNull() != null)
159 .forEach(invoke -> {
160 MethodRef methodRef = invoke.op().invokeReference();
161 Optional.ofNullable(mapFunctions.get(methodRef))
162 .ifPresentOrElse(_ ->
163 addEdge(entryPoint, invoke, mapFunctions),
164 () -> addEdge(entryPoint, invoke));
165 });
166
167 closeRanks();
168 }
169 }