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