1 /*
2  * Copyright (c) 2016, 2018, 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 
26 package jdk.tools.jaotc;
27 
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Set;
31 
32 import jdk.tools.jaotc.AOTDynamicTypeStore.AdapterLocation;
33 import jdk.tools.jaotc.AOTDynamicTypeStore.AppendixLocation;
34 import jdk.tools.jaotc.AOTDynamicTypeStore.Location;
35 import jdk.tools.jaotc.binformat.BinaryContainer;
36 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
37 import jdk.tools.jaotc.binformat.Symbol.Binding;
38 import jdk.tools.jaotc.binformat.Symbol.Kind;
39 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
40 import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
41 import jdk.vm.ci.meta.ResolvedJavaField;
42 import jdk.vm.ci.meta.ResolvedJavaMethod;
43 import jdk.vm.ci.meta.ResolvedJavaType;
44 
45 /**
46  * Class encapsulating Graal-compiled output of a Java class. The compilation result of all methods
47  * of a class {@code className} are maintained in an array list.
48  */
49 final class AOTCompiledClass {
50 
51     private static AOTDynamicTypeStore dynoStore;
52 
setDynamicTypeStore(AOTDynamicTypeStore s)53     static void setDynamicTypeStore(AOTDynamicTypeStore s) {
54         dynoStore = s;
55     }
56 
57     static class AOTKlassData {
58         private int gotIndex; // Index (offset/8) to the got in the .metaspace.got section
59         private int classId;  // Unique ID
60         // Offset to compiled methods data in the .methods.offsets section.
61         private int compiledMethodsOffset;
62         // Offset to dependent methods data.
63         private int dependentMethodsOffset;
64 
65         private final String metadataName;
66         HotSpotResolvedObjectType type;
67 
68         /**
69          * List of dependent compiled methods which have a reference to this class.
70          */
71         private ArrayList<CompiledMethodInfo> dependentMethods;
72 
AOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type, int classId)73         AOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type, int classId) {
74             this.dependentMethods = new ArrayList<>();
75             this.classId = classId;
76             this.type = type;
77             this.metadataName = type.isAnonymous() ? "anon<" + classId + ">" : type.getName();
78             this.gotIndex = binaryContainer.addTwoSlotKlassSymbol(metadataName);
79             this.compiledMethodsOffset = -1; // Not compiled classes do not have compiled methods.
80             this.dependentMethodsOffset = -1;
81         }
82 
getMetaspaceNames()83         private String[] getMetaspaceNames() {
84             String name = metadataName;
85             Set<Location> locs = dynoStore.getDynamicClassLocationsForType(type);
86             if (locs == null) {
87                 return new String[]{name};
88             } else {
89                 ArrayList<String> names = new ArrayList<>();
90                 names.add(name);
91                 for (Location l : locs) {
92                     HotSpotResolvedObjectType cpType = l.getHolder();
93                     AOTKlassData data = getAOTKlassData(cpType);
94                     // We collect dynamic types at parse time, but late inlining
95                     // may record types that don't make it into the final graph.
96                     // We can safely ignore those here.
97                     if (data == null) {
98                         // Not a compiled or inlined method
99                         continue;
100                     }
101                     int cpi = l.getCpi();
102                     String location = "<" + data.classId + ":" + cpi + ">";
103                     if (l instanceof AdapterLocation) {
104                         names.add("adapter" + location);
105                         AdapterLocation a = (AdapterLocation) l;
106                         names.add("adapter:" + a.getMethodId() + location);
107                     } else {
108                         assert l instanceof AppendixLocation;
109                         names.add("appendix" + location);
110                     }
111                 }
112                 return names.toArray(new String[names.size()]);
113             }
114         }
115 
getType()116         HotSpotResolvedObjectType getType() {
117             return type;
118         }
119 
getMetadataName()120         String getMetadataName() {
121             return metadataName;
122         }
123 
124         /**
125          * Add a method to the list of dependent methods.
126          */
addDependentMethod(CompiledMethodInfo cm)127         synchronized boolean addDependentMethod(CompiledMethodInfo cm) {
128             return dependentMethods.add(cm);
129         }
130 
131         /**
132          * Return the array list of dependent class methods.
133          *
134          * @return array list of dependent methods
135          */
getDependentMethods()136         ArrayList<CompiledMethodInfo> getDependentMethods() {
137             return dependentMethods;
138         }
139 
140         /**
141          * Returns if this class has dependent methods.
142          *
143          * @return true if dependent methods exist, false otherwise
144          */
hasDependentMethods()145         boolean hasDependentMethods() {
146             return !dependentMethods.isEmpty();
147         }
148 
setCompiledMethodsOffset(int offset)149         void setCompiledMethodsOffset(int offset) {
150             compiledMethodsOffset = offset;
151         }
152 
putAOTKlassData(BinaryContainer binaryContainer, ReadOnlyDataContainer container)153         protected void putAOTKlassData(BinaryContainer binaryContainer, ReadOnlyDataContainer container) {
154             int cntDepMethods = dependentMethods.size();
155             // Create array of dependent methods IDs. First word is count.
156             ReadOnlyDataContainer dependenciesContainer = binaryContainer.getKlassesDependenciesContainer();
157             this.dependentMethodsOffset = BinaryContainer.addMethodsCount(cntDepMethods, dependenciesContainer);
158             for (CompiledMethodInfo methodInfo : dependentMethods) {
159                 dependenciesContainer.appendInt(methodInfo.getCodeId());
160             }
161 
162             verify();
163 
164             // @formatter:off
165             /*
166              * The offsets layout should match AOTKlassData structure in AOT JVM runtime
167              */
168             int offset = container.getByteStreamSize();
169             for (String name : getMetaspaceNames()) {
170                 container.createSymbol(offset, Kind.OBJECT, Binding.GLOBAL, 0, name);
171             }
172                       // Add index (offset/8) to the got in the .metaspace.got section
173             container.appendInt(gotIndex).
174                       // Add unique ID
175                       appendInt(classId).
176                       // Add the offset to compiled methods data in the .metaspace.offsets section.
177                       appendInt(compiledMethodsOffset).
178                       // Add the offset to dependent methods data in the .metaspace.offsets section.
179                       appendInt(dependentMethodsOffset).
180                       // Add fingerprint.
181                       appendLong(type.getFingerprint());
182 
183             // @formatter:on
184         }
185 
verify()186         private void verify() {
187             String name = type.getName();
188             assert gotIndex > 0 : "incorrect gotIndex: " + gotIndex + " for klass: " + name;
189             long fingerprint = type.getFingerprint();
190             assert type.isArray() || fingerprint != 0 : "incorrect fingerprint: " + fingerprint + " for klass: " + name;
191             assert compiledMethodsOffset >= -1 : "incorrect compiledMethodsOffset: " + compiledMethodsOffset + " for klass: " + name;
192             assert dependentMethodsOffset >= -1 : "incorrect dependentMethodsOffset: " + dependentMethodsOffset + " for klass: " + name;
193             assert classId >= 0 : "incorrect classId: " + classId + " for klass: " + name;
194         }
195 
196     }
197 
198     private final HotSpotResolvedObjectType resolvedJavaType;
199 
200     /**
201      * List of all collected class data.
202      */
203     private static HashMap<String, AOTKlassData> klassData = new HashMap<>();
204 
205     /**
206      * List of all methods to be compiled.
207      */
208     private ArrayList<ResolvedJavaMethod> methods = new ArrayList<>();
209 
210     /**
211      * List of all compiled class methods.
212      */
213     private ArrayList<CompiledMethodInfo> compiledMethods;
214 
215     /**
216      * If this class represents Graal stub code.
217      */
218     private final boolean representsStubs;
219 
220     /**
221      * Classes count used to generate unique global method id.
222      */
223     private static int classesCount = 0;
224 
225     /**
226      * Construct an object with compiled methods. Intended to be used for code with no corresponding
227      * Java method name in the user application.
228      *
229      * @param compiledMethods AOT compiled methods
230      */
AOTCompiledClass(ArrayList<CompiledMethodInfo> compiledMethods)231     AOTCompiledClass(ArrayList<CompiledMethodInfo> compiledMethods) {
232         this.resolvedJavaType = null;
233         this.compiledMethods = compiledMethods;
234         this.representsStubs = true;
235     }
236 
237     /**
238      * Construct an object with compiled versions of the named class.
239      */
AOTCompiledClass(ResolvedJavaType resolvedJavaType)240     AOTCompiledClass(ResolvedJavaType resolvedJavaType) {
241         this.resolvedJavaType = (HotSpotResolvedObjectType) resolvedJavaType;
242         this.compiledMethods = new ArrayList<>();
243         this.representsStubs = false;
244     }
245 
246     /**
247      * @return the ResolvedJavaType of this class
248      */
getResolvedJavaType()249     ResolvedJavaType getResolvedJavaType() {
250         return resolvedJavaType;
251     }
252 
253     /**
254      * Get the list of methods which should be compiled.
255      */
getMethods()256     ArrayList<ResolvedJavaMethod> getMethods() {
257         ArrayList<ResolvedJavaMethod> m = methods;
258         methods = null; // Free - it is not used after that.
259         return m;
260     }
261 
262     /**
263      * Get the number of all AOT classes.
264      */
getClassesCount()265     static int getClassesCount() {
266         return classesCount;
267     }
268 
269     /**
270      * Get the number of methods which should be compiled.
271      *
272      * @return number of methods which should be compiled
273      */
getMethodCount()274     int getMethodCount() {
275         return methods.size();
276     }
277 
278     /**
279      * Add a method to the list of methods to be compiled.
280      */
addMethod(ResolvedJavaMethod method)281     void addMethod(ResolvedJavaMethod method) {
282         methods.add(method);
283     }
284 
285     /**
286      * Returns if this class has methods which should be compiled.
287      *
288      * @return true if this class contains methods which should be compiled, false otherwise
289      */
hasMethods()290     boolean hasMethods() {
291         return !methods.isEmpty();
292     }
293 
294     /**
295      * Add a method to the list of compiled methods. This method needs to be thread-safe.
296      */
addCompiledMethod(CompiledMethodInfo cm)297     synchronized boolean addCompiledMethod(CompiledMethodInfo cm) {
298         return compiledMethods.add(cm);
299     }
300 
301     /**
302      * Return the array list of compiled class methods.
303      *
304      * @return array list of compiled methods
305      */
getCompiledMethods()306     ArrayList<CompiledMethodInfo> getCompiledMethods() {
307         return compiledMethods;
308     }
309 
310     /**
311      * Returns if this class has successfully compiled methods.
312      *
313      * @return true if methods were compiled, false otherwise
314      */
hasCompiledMethods()315     boolean hasCompiledMethods() {
316         return !compiledMethods.isEmpty();
317     }
318 
319     /**
320      * Add a klass data.
321      */
addAOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type)322     static synchronized AOTKlassData addAOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) {
323         AOTKlassData data = getAOTKlassData(type);
324         if (data == null) {
325             data = new AOTKlassData(binaryContainer, type, classesCount++);
326             klassData.put(type.getName(), data);
327         }
328         return data;
329     }
330 
getAOTKlassData(HotSpotResolvedObjectType type)331     static synchronized AOTKlassData getAOTKlassData(HotSpotResolvedObjectType type) {
332         String name = type.getName();
333         AOTKlassData data = klassData.get(name);
334         if (data != null) {
335             HotSpotResolvedObjectType oldType = data.getType();
336             assert oldType.equals(type) : "duplicate classes for name " + type.getName();
337         }
338         return data;
339     }
340 
addAOTKlassData(BinaryContainer binaryContainer)341     void addAOTKlassData(BinaryContainer binaryContainer) {
342         for (CompiledMethodInfo methodInfo : compiledMethods) {
343             // Record methods holder
344             methodInfo.addDependentKlassData(binaryContainer, resolvedJavaType);
345             // Record inlinee classes
346             ResolvedJavaMethod[] inlinees = methodInfo.getCompilationResult().getMethods();
347             if (inlinees != null) {
348                 for (ResolvedJavaMethod m : inlinees) {
349                     methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) m.getDeclaringClass());
350                 }
351             }
352             // Record classes of fields that were accessed
353             ResolvedJavaField[] fields = methodInfo.getCompilationResult().getFields();
354             if (fields != null) {
355                 for (ResolvedJavaField f : fields) {
356                     methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) f.getDeclaringClass());
357                 }
358             }
359         }
360     }
361 
addFingerprintKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type)362     static synchronized AOTKlassData addFingerprintKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) {
363         if (type.isArray()) {
364             return addAOTKlassData(binaryContainer, type);
365         }
366         assert type.getFingerprint() != 0 : "no fingerprint for " + type.getName();
367         AOTKlassData old = getAOTKlassData(type);
368         if (old != null) {
369             if (areAssertionsEnabled()) {
370                 HotSpotResolvedObjectType s = type.getSuperclass();
371                 if (s != null) {
372                     assert getAOTKlassData(s) != null : "fingerprint for super " + s.getName() + " needed for " + type.getName();
373                 }
374                 for (HotSpotResolvedObjectType i : type.getInterfaces()) {
375                     assert getAOTKlassData(i) != null : "fingerprint for interface " + i.getName() + " needed for " + type.getName();
376                 }
377             }
378             return old;
379         }
380 
381         // Fingerprinting requires super classes and super interfaces
382         HotSpotResolvedObjectType s = type.getSuperclass();
383         if (s != null) {
384             addFingerprintKlassData(binaryContainer, s);
385         }
386         for (HotSpotResolvedObjectType i : type.getInterfaces()) {
387             addFingerprintKlassData(binaryContainer, i);
388         }
389 
390         return addAOTKlassData(binaryContainer, type);
391     }
392 
393     @SuppressWarnings("all")
areAssertionsEnabled()394     private static boolean areAssertionsEnabled() {
395         boolean assertsEnabled = false;
396         // Next assignment will be executed when asserts are enabled.
397         assert assertsEnabled = true;
398         return assertsEnabled;
399     }
400 
401     /*
402      * Put methods data to contained.
403      */
putMethodsData(BinaryContainer binaryContainer)404     void putMethodsData(BinaryContainer binaryContainer) {
405         ReadOnlyDataContainer container = binaryContainer.getMethodsOffsetsContainer();
406         int cntMethods = compiledMethods.size();
407         int startMethods = BinaryContainer.addMethodsCount(cntMethods, container);
408         for (CompiledMethodInfo methodInfo : compiledMethods) {
409             methodInfo.addMethodOffsets(binaryContainer, container);
410         }
411         String name = resolvedJavaType.getName();
412         AOTKlassData data = getAOTKlassData(resolvedJavaType);
413         assert data != null : "missing data for klass: " + name;
414         int cntDepMethods = data.dependentMethods.size();
415         assert cntDepMethods > 0 : "no dependent methods for compiled klass: " + name;
416         data.setCompiledMethodsOffset(startMethods);
417     }
418 
putAOTKlassData(BinaryContainer binaryContainer)419     static void putAOTKlassData(BinaryContainer binaryContainer) {
420         // record dynamic types
421         Set<HotSpotResolvedObjectType> dynoTypes = dynoStore.getDynamicTypes();
422         if (dynoTypes != null) {
423             for (HotSpotResolvedObjectType dynoType : dynoTypes) {
424                 addFingerprintKlassData(binaryContainer, dynoType);
425             }
426         }
427 
428         ReadOnlyDataContainer container = binaryContainer.getKlassesOffsetsContainer();
429         for (AOTKlassData data : klassData.values()) {
430             data.putAOTKlassData(binaryContainer, container);
431         }
432     }
433 
getType(Object ref)434     static HotSpotResolvedObjectType getType(Object ref) {
435         return (ref instanceof HotSpotResolvedObjectType) ? (HotSpotResolvedObjectType) ref : ((HotSpotResolvedJavaMethod) ref).getDeclaringClass();
436     }
437 
metadataName(HotSpotResolvedObjectType type)438     static String metadataName(HotSpotResolvedObjectType type) {
439         AOTKlassData data = getAOTKlassData(type);
440         assert data != null : "no data for " + type;
441         return getAOTKlassData(type).getMetadataName();
442     }
443 
metadataName(HotSpotResolvedJavaMethod m)444     private static String metadataName(HotSpotResolvedJavaMethod m) {
445         return metadataName(m.getDeclaringClass()) + "." + m.getName() + m.getSignature().toMethodDescriptor();
446     }
447 
metadataName(Object ref)448     static String metadataName(Object ref) {
449         if (ref instanceof HotSpotResolvedJavaMethod) {
450             HotSpotResolvedJavaMethod m = (HotSpotResolvedJavaMethod) ref;
451             return metadataName(m);
452         } else {
453             assert ref instanceof HotSpotResolvedObjectType : "unexpected object type " + ref.getClass().getName();
454             HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) ref;
455             return metadataName(type);
456         }
457     }
458 
representsStubs()459     boolean representsStubs() {
460         return representsStubs;
461     }
462 
clear()463     void clear() {
464         for (CompiledMethodInfo c : compiledMethods) {
465             c.clear();
466         }
467         this.compiledMethods = null;
468         this.methods = null;
469     }
470 
471 }
472