1/*
2 * Copyright (c) 2016, 2017, 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
24package jdk.tools.jaotc;
25
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.Map;
29
30import jdk.tools.jaotc.binformat.BinaryContainer;
31import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
32import jdk.tools.jaotc.binformat.Symbol.Binding;
33import jdk.tools.jaotc.binformat.Symbol.Kind;
34
35import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
36import jdk.vm.ci.meta.ResolvedJavaField;
37import jdk.vm.ci.meta.ResolvedJavaMethod;
38import jdk.vm.ci.meta.ResolvedJavaType;
39
40/**
41 * Class encapsulating Graal-compiled output of a Java class. The compilation result of all methods
42 * of a class {@code className} are maintained in an array list.
43 */
44final class AOTCompiledClass {
45
46    static class AOTKlassData {
47        private int gotIndex; // Index (offset/8) to the got in the .metaspace.got section
48        private int classId;  // Unique ID
49        // Offset to compiled methods data in the .methods.offsets section.
50        private int compiledMethodsOffset;
51        // Offset to dependent methods data.
52        private int dependentMethodsOffset;
53        private long fingerprint;           // Class fingerprint
54
55        private final String name;
56        private boolean isArray;
57
58        /**
59         * List of dependent compiled methods which have a reference to this class.
60         */
61        private ArrayList<CompiledMethodInfo> dependentMethods;
62
63        AOTKlassData(BinaryContainer binaryContainer, String name, long fingerprint, int classId) {
64            this.dependentMethods = new ArrayList<>();
65            this.classId = classId;
66            this.fingerprint = fingerprint;
67            this.gotIndex = binaryContainer.addTwoSlotKlassSymbol(name);
68            this.compiledMethodsOffset = -1; // Not compiled classes do not have compiled methods.
69            this.dependentMethodsOffset = -1;
70            this.name = name;
71            this.isArray = name.length() > 0 && name.charAt(0) == '[';
72        }
73
74        long getFingerprint() {
75            return fingerprint;
76        }
77
78        /**
79         * Add a method to the list of dependent methods.
80         */
81        synchronized boolean addDependentMethod(CompiledMethodInfo cm) {
82            return dependentMethods.add(cm);
83        }
84
85        /**
86         * Return the array list of dependent class methods.
87         *
88         * @return array list of dependent methods
89         */
90        ArrayList<CompiledMethodInfo> getDependentMethods() {
91            return dependentMethods;
92        }
93
94        /**
95         * Returns if this class has dependent methods.
96         *
97         * @return true if dependent methods exist, false otherwise
98         */
99        boolean hasDependentMethods() {
100            return !dependentMethods.isEmpty();
101        }
102
103        void setCompiledMethodsOffset(int offset) {
104            compiledMethodsOffset = offset;
105        }
106
107        protected void putAOTKlassData(BinaryContainer binaryContainer, ReadOnlyDataContainer container) {
108            int cntDepMethods = dependentMethods.size();
109            // Create array of dependent methods IDs. First word is count.
110            ReadOnlyDataContainer dependenciesContainer = binaryContainer.getKlassesDependenciesContainer();
111            this.dependentMethodsOffset = BinaryContainer.addMethodsCount(cntDepMethods, dependenciesContainer);
112            for (CompiledMethodInfo methodInfo : dependentMethods) {
113                dependenciesContainer.appendInt(methodInfo.getCodeId());
114            }
115            verify();
116
117            // @formatter:off
118            /*
119             * The offsets layout should match AOTKlassData structure in AOT JVM runtime
120             */
121            int offset = container.getByteStreamSize();
122            container.createSymbol(offset, Kind.OBJECT, Binding.GLOBAL, 0, name);
123                      // Add index (offset/8) to the got in the .metaspace.got section
124            container.appendInt(gotIndex).
125                      // Add unique ID
126                      appendInt(classId).
127                      // Add the offset to compiled methods data in the .metaspace.offsets section.
128                      appendInt(compiledMethodsOffset).
129                      // Add the offset to dependent methods data in the .metaspace.offsets section.
130                      appendInt(dependentMethodsOffset).
131                      // Add fingerprint.
132                      appendLong(fingerprint);
133            // @formatter:on
134        }
135
136        private void verify() {
137            assert gotIndex > 0 : "incorrect gotIndex: " + gotIndex + " for klass: " + name;
138            assert isArray || fingerprint != 0 : "incorrect fingerprint: " + fingerprint + " for klass: " + name;
139            assert compiledMethodsOffset >= -1 : "incorrect compiledMethodsOffset: " + compiledMethodsOffset + " for klass: " + name;
140            assert dependentMethodsOffset >= -1 : "incorrect dependentMethodsOffset: " + dependentMethodsOffset + " for klass: " + name;
141            assert classId >= 0 : "incorrect classId: " + classId + " for klass: " + name;
142        }
143
144    }
145
146    private final HotSpotResolvedObjectType resolvedJavaType;
147
148    /**
149     * List of all collected class data.
150     */
151    private static Map<String, AOTKlassData> klassData = new HashMap<>();
152
153    /**
154     * List of all methods to be compiled.
155     */
156    private ArrayList<ResolvedJavaMethod> methods = new ArrayList<>();
157
158    /**
159     * List of all compiled class methods.
160     */
161    private ArrayList<CompiledMethodInfo> compiledMethods;
162
163    /**
164     * If this class represents Graal stub code.
165     */
166    private final boolean representsStubs;
167
168    /**
169     * Classes count used to generate unique global method id.
170     */
171    private static int classesCount = 0;
172
173    /**
174     * Construct an object with compiled methods. Intended to be used for code with no corresponding
175     * Java method name in the user application.
176     *
177     * @param compiledMethods AOT compiled methods
178     */
179    AOTCompiledClass(ArrayList<CompiledMethodInfo> compiledMethods) {
180        this.resolvedJavaType = null;
181        this.compiledMethods = compiledMethods;
182        this.representsStubs = true;
183    }
184
185    /**
186     * Construct an object with compiled versions of the named class.
187     */
188    AOTCompiledClass(ResolvedJavaType resolvedJavaType) {
189        this.resolvedJavaType = (HotSpotResolvedObjectType) resolvedJavaType;
190        this.compiledMethods = new ArrayList<>();
191        this.representsStubs = false;
192    }
193
194    /**
195     * @return the ResolvedJavaType of this class
196     */
197    ResolvedJavaType getResolvedJavaType() {
198        return resolvedJavaType;
199    }
200
201    /**
202     * Get the list of methods which should be compiled.
203     */
204    ArrayList<ResolvedJavaMethod> getMethods() {
205        ArrayList<ResolvedJavaMethod> m = methods;
206        methods = null; // Free - it is not used after that.
207        return m;
208    }
209
210    /**
211     * Get the number of all AOT classes.
212     */
213    static int getClassesCount() {
214        return classesCount;
215    }
216
217    /**
218     * Get the number of methods which should be compiled.
219     *
220     * @return number of methods which should be compiled
221     */
222    int getMethodCount() {
223        return methods.size();
224    }
225
226    /**
227     * Add a method to the list of methods to be compiled.
228     */
229    void addMethod(ResolvedJavaMethod method) {
230        methods.add(method);
231    }
232
233    /**
234     * Returns if this class has methods which should be compiled.
235     *
236     * @return true if this class contains methods which should be compiled, false otherwise
237     */
238    boolean hasMethods() {
239        return !methods.isEmpty();
240    }
241
242    /**
243     * Add a method to the list of compiled methods. This method needs to be thread-safe.
244     */
245    synchronized boolean addCompiledMethod(CompiledMethodInfo cm) {
246        return compiledMethods.add(cm);
247    }
248
249    /**
250     * Return the array list of compiled class methods.
251     *
252     * @return array list of compiled methods
253     */
254    ArrayList<CompiledMethodInfo> getCompiledMethods() {
255        return compiledMethods;
256    }
257
258    /**
259     * Returns if this class has successfully compiled methods.
260     *
261     * @return true if methods were compiled, false otherwise
262     */
263    boolean hasCompiledMethods() {
264        return !compiledMethods.isEmpty();
265    }
266
267    /**
268     * Add a klass data.
269     */
270    synchronized static AOTKlassData addAOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) {
271        String name = type.getName();
272        long fingerprint = type.getFingerprint();
273        AOTKlassData data = klassData.get(name);
274        if (data != null) {
275            assert data.getFingerprint() == fingerprint : "incorrect fingerprint data for klass: " + name;
276        } else {
277            data = new AOTKlassData(binaryContainer, name, fingerprint, classesCount++);
278            klassData.put(name, data);
279        }
280        return data;
281    }
282
283    synchronized static AOTKlassData getAOTKlassData(String name) {
284        return klassData.get(name);
285    }
286
287    synchronized static AOTKlassData getAOTKlassData(HotSpotResolvedObjectType type) {
288        return getAOTKlassData(type.getName());
289    }
290
291    void addAOTKlassData(BinaryContainer binaryContainer) {
292        for (CompiledMethodInfo methodInfo : compiledMethods) {
293            // Record methods holder
294            methodInfo.addDependentKlassData(binaryContainer, resolvedJavaType);
295            // Record inlinee classes
296            ResolvedJavaMethod[] inlinees = methodInfo.getCompilationResult().getMethods();
297            if (inlinees != null) {
298                for (ResolvedJavaMethod m : inlinees) {
299                    methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) m.getDeclaringClass());
300                }
301            }
302            // Record classes of fields that were accessed
303            ResolvedJavaField[] fields = methodInfo.getCompilationResult().getFields();
304            if (fields != null) {
305                for (ResolvedJavaField f : fields) {
306                    methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) f.getDeclaringClass());
307                }
308            }
309        }
310    }
311
312    synchronized static AOTKlassData addFingerprintKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) {
313        if (type.isArray()) {
314            return addAOTKlassData(binaryContainer, type);
315        }
316        assert type.getFingerprint() != 0 : "no fingerprint for " + type.getName();
317        AOTKlassData old = getAOTKlassData(type);
318        if (old != null) {
319            boolean assertsEnabled = false;
320            // Next assignment will be executed when asserts are enabled.
321            assert assertsEnabled = true;
322            if (assertsEnabled) {
323                HotSpotResolvedObjectType s = type.getSuperclass();
324                if (s != null) {
325                    assert getAOTKlassData(s) != null : "fingerprint for super " + s.getName() + " needed for " + type.getName();
326                }
327                for (HotSpotResolvedObjectType i : type.getInterfaces()) {
328                    assert getAOTKlassData(i) != null : "fingerprint for interface " + i.getName() + " needed for " + type.getName();
329                }
330            }
331            return old;
332        }
333
334        // Fingerprinting requires super classes and super interfaces
335        HotSpotResolvedObjectType s = type.getSuperclass();
336        if (s != null) {
337            addFingerprintKlassData(binaryContainer, s);
338        }
339        for (HotSpotResolvedObjectType i : type.getInterfaces()) {
340            addFingerprintKlassData(binaryContainer, i);
341        }
342
343        return addAOTKlassData(binaryContainer, type);
344    }
345
346    /*
347     * Put methods data to contained.
348     */
349    void putMethodsData(BinaryContainer binaryContainer) {
350        ReadOnlyDataContainer container = binaryContainer.getMethodsOffsetsContainer();
351        int cntMethods = compiledMethods.size();
352        int startMethods = BinaryContainer.addMethodsCount(cntMethods, container);
353        for (CompiledMethodInfo methodInfo : compiledMethods) {
354            methodInfo.addMethodOffsets(binaryContainer, container);
355        }
356        String name = resolvedJavaType.getName();
357        AOTKlassData data = klassData.get(name);
358        assert data != null : "missing data for klass: " + name;
359        assert data.getFingerprint() == resolvedJavaType.getFingerprint() : "incorrect fingerprint for klass: " + name;
360        int cntDepMethods = data.dependentMethods.size();
361        assert cntDepMethods > 0 : "no dependent methods for compiled klass: " + name;
362        data.setCompiledMethodsOffset(startMethods);
363    }
364
365    static void putAOTKlassData(BinaryContainer binaryContainer) {
366        ReadOnlyDataContainer container = binaryContainer.getKlassesOffsetsContainer();
367        for (AOTKlassData data : klassData.values()) {
368            data.putAOTKlassData(binaryContainer, container);
369        }
370    }
371
372    boolean representsStubs() {
373        return representsStubs;
374    }
375
376    void clear() {
377        for (CompiledMethodInfo c : compiledMethods) {
378            c.clear();
379        }
380        this.compiledMethods = null;
381        this.methods = null;
382    }
383
384}
385