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 }