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.List;
29import java.util.Map.Entry;
30
31import jdk.tools.jaotc.binformat.BinaryContainer;
32import jdk.tools.jaotc.binformat.ByteContainer;
33import jdk.tools.jaotc.binformat.HeaderContainer;
34
35import org.graalvm.compiler.code.CompilationResult;
36import org.graalvm.compiler.debug.DebugContext;
37import org.graalvm.compiler.hotspot.HotSpotHostBackend;
38import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider;
39import org.graalvm.compiler.hotspot.stubs.Stub;
40
41import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
42import jdk.vm.ci.hotspot.HotSpotVMConfigStore;
43import jdk.vm.ci.hotspot.VMField;
44
45final class DataBuilder {
46
47    private final Main main;
48
49    private final HotSpotHostBackend backend;
50
51    private final List<AOTCompiledClass> classes;
52
53    /**
54     * Target-independent container in which text symbols and code bytes are created.
55     */
56    private final BinaryContainer binaryContainer;
57
58    private static final HashMap<Long, String> vmAddresses = new HashMap<>();
59
60    DataBuilder(Main main, HotSpotHostBackend backend, List<AOTCompiledClass> classes, BinaryContainer binaryContainer) {
61        this.main = main;
62        this.backend = backend;
63        this.classes = classes;
64        this.binaryContainer = binaryContainer;
65        fillVMAddresses(HotSpotJVMCIRuntime.runtime().getConfigStore());
66    }
67
68    /**
69     * Returns a value-name map of all {@link VMField} fields.
70     */
71    private static void fillVMAddresses(HotSpotVMConfigStore config) {
72        for (VMField vmField : config.getFields().values()) {
73            if (vmField.value != null && vmField.value instanceof Long) {
74                final long address = (Long) vmField.value;
75                String value = vmField.name;
76                /*
77                 * Some fields don't contain addresses but integer values. At least don't add zero
78                 * entries to avoid matching null addresses.
79                 */
80                if (address != 0) {
81                    vmAddresses.put(address, value);
82                }
83            }
84        }
85        for (Entry<String, Long> vmAddress : config.getAddresses().entrySet()) {
86            final long address = vmAddress.getValue();
87            String value = vmAddress.getKey();
88            String old = vmAddresses.put(address, value);
89            if (old != null) {
90                throw new InternalError("already in map: address: " + address + ", current: " + value + ", old: " + old);
91            }
92        }
93    }
94
95    /**
96     * Get the C/C++ function name associated with the foreign call target {@code address}.
97     *
98     * @param address native address
99     * @return C/C++ functio name associated with the native address
100     */
101    static String getVMFunctionNameForAddress(long address) {
102        return vmAddresses.get(address);
103    }
104
105    /**
106     * Returns the host backend used for this compilation.
107     *
108     * @return host backend
109     */
110    HotSpotHostBackend getBackend() {
111        return backend;
112    }
113
114    /**
115     * Returns the binary container for this compilation.
116     *
117     * @return binary container
118     */
119    BinaryContainer getBinaryContainer() {
120        return binaryContainer;
121    }
122
123    /**
124     * Prepare data with all compiled classes and stubs.
125     *
126     * @param debug
127     *
128     * @throws Exception
129     */
130    @SuppressWarnings("try")
131    void prepareData(DebugContext debug) throws Exception {
132        try (Timer t = new Timer(main, "Parsing compiled code")) {
133            /*
134             * Copy compiled code into code section container and calls stubs (PLT trampoline).
135             */
136            CodeSectionProcessor codeSectionProcessor = new CodeSectionProcessor(this);
137            for (AOTCompiledClass c : classes) {
138                // For each class we need 2 GOT slots:
139                // first - for initialized klass
140                // second - only for loaded klass
141                c.addAOTKlassData(binaryContainer);
142                codeSectionProcessor.process(c);
143            }
144        }
145
146        AOTCompiledClass stubCompiledCode = retrieveStubCode(debug);
147
148        // Free memory!
149        try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
150            main.printer.printMemoryUsage();
151            System.gc();
152        }
153
154        MetadataBuilder metadataBuilder = null;
155        try (Timer t = new Timer(main, "Processing metadata")) {
156            /*
157             * Generate metadata for compiled code and copy it into metadata section. Create
158             * relocation information for all references (call, constants, etc) in compiled code.
159             */
160            metadataBuilder = new MetadataBuilder(this);
161            metadataBuilder.processMetadata(classes, stubCompiledCode);
162        }
163
164        // Free memory!
165        try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
166            main.printer.printMemoryUsage();
167            System.gc();
168        }
169
170        try (Timer t = new Timer(main, "Preparing stubs binary")) {
171            prepareStubsBinary(stubCompiledCode);
172        }
173        try (Timer t = new Timer(main, "Preparing compiled binary")) {
174            // Should be called after Stubs because they can set dependent klasses.
175            prepareCompiledBinary();
176        }
177    }
178
179    /**
180     * Get all stubs from Graal and add them to the code section.
181     *
182     * @param debug
183     */
184    @SuppressWarnings("try")
185    private AOTCompiledClass retrieveStubCode(DebugContext debug) {
186        ArrayList<CompiledMethodInfo> stubs = new ArrayList<>();
187        HotSpotForeignCallsProvider foreignCallsProvider = backend.getProviders().getForeignCalls();
188        for (Stub stub : foreignCallsProvider.getStubs()) {
189            try (DebugContext.Scope scope = debug.scope("CompileStubs")) {
190                CompilationResult result = stub.getCompilationResult(debug, backend);
191                CompiledMethodInfo cm = new CompiledMethodInfo(result, new AOTStub(stub, backend));
192                stubs.add(cm);
193            } catch (Throwable e) {
194                throw debug.handle(e);
195            }
196        }
197        AOTCompiledClass stubCompiledCode = new AOTCompiledClass(stubs);
198        CodeSectionProcessor codeSectionProcessor = new CodeSectionProcessor(this);
199        codeSectionProcessor.process(stubCompiledCode);
200        return stubCompiledCode;
201    }
202
203    /**
204     * Prepare metaspace.offsets section.
205     */
206    private void prepareCompiledBinary() {
207        for (AOTCompiledClass c : classes) {
208            // Create records for compiled AOT methods.
209            c.putMethodsData(binaryContainer);
210        }
211        // Create records for compiled AOT classes.
212        AOTCompiledClass.putAOTKlassData(binaryContainer);
213
214        // Fill in AOTHeader
215        HeaderContainer header = binaryContainer.getHeaderContainer();
216        header.setClassesCount(AOTCompiledClass.getClassesCount());
217        header.setMethodsCount(CompiledMethodInfo.getMethodsCount());
218        // Record size of got sections
219        ByteContainer bc = binaryContainer.getKlassesGotContainer();
220        header.setKlassesGotSize((bc.getByteStreamSize() / 8));
221        bc = binaryContainer.getMetadataGotContainer();
222        header.setMetadataGotSize((bc.getByteStreamSize() / 8));
223        bc = binaryContainer.getOopGotContainer();
224        header.setOopGotSize((bc.getByteStreamSize() / 8));
225    }
226
227    /**
228     * Prepare stubs.offsets section.
229     */
230    private void prepareStubsBinary(AOTCompiledClass compiledClass) {
231        // For each of the compiled stubs, create records holding information about
232        // them.
233        ArrayList<CompiledMethodInfo> compiledStubs = compiledClass.getCompiledMethods();
234        int cntStubs = compiledStubs.size();
235        BinaryContainer.addMethodsCount(cntStubs, binaryContainer.getStubsOffsetsContainer());
236        for (CompiledMethodInfo methodInfo : compiledStubs) {
237            // Note, stubs have different offsets container.
238            methodInfo.addMethodOffsets(binaryContainer, binaryContainer.getStubsOffsetsContainer());
239        }
240    }
241
242}
243