1 /*
   2  * Copyright (c) 2016, 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 package jdk.tools.jaotc;
  25 
  26 import java.util.ArrayList;
  27 import java.util.HashMap;
  28 import java.util.List;
  29 import java.util.Map.Entry;
  30 
  31 import jdk.tools.jaotc.binformat.BinaryContainer;
  32 import jdk.tools.jaotc.binformat.ByteContainer;
  33 import jdk.tools.jaotc.binformat.HeaderContainer;
  34 import jdk.tools.jaotc.utils.Timer;
  35 import org.graalvm.compiler.code.CompilationResult;
  36 import org.graalvm.compiler.debug.Debug;
  37 import org.graalvm.compiler.debug.Debug.Scope;
  38 import org.graalvm.compiler.hotspot.HotSpotHostBackend;
  39 import org.graalvm.compiler.hotspot.stubs.Stub;
  40 
  41 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
  42 import jdk.vm.ci.hotspot.HotSpotVMConfigStore;
  43 import jdk.vm.ci.hotspot.VMField;
  44 
  45 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 final HashMap<Long, String> vmAddresses = new HashMap<>();
  59 
  60     public 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 void fillVMAddresses(HotSpotVMConfigStore config) {
  72         for (VMField vmField : config.getFields().values()) {
  73             if (vmField.value != null) {
  74                 final long address = 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     public 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     public HotSpotHostBackend getBackend() {
 111         return backend;
 112     }
 113 
 114     /**
 115      * Returns the binary container for this compilation.
 116      *
 117      * @return binary container
 118      */
 119     public BinaryContainer getBinaryContainer() {
 120         return binaryContainer;
 121     }
 122 
 123     /**
 124      * Prepare data with all compiled classes and stubs.
 125      *
 126      * @throws Exception
 127      */
 128     @SuppressWarnings("try")
 129     public void prepareData() throws Exception {
 130         try (Timer t = new Timer(main, "Parsing compiled code")) {
 131             /*
 132              * Copy compiled code into code section container and calls stubs (PLT trampoline).
 133              */
 134             CodeSectionProcessor codeSectionProcessor = new CodeSectionProcessor(this);
 135             for (AOTCompiledClass c : classes) {
 136                 // For each class we need 2 GOT slots:
 137                 // first - for initialized klass
 138                 // second - only for loaded klass
 139                 c.addAOTKlassData(binaryContainer);
 140                 codeSectionProcessor.process(c);
 141             }
 142         }
 143 
 144         AOTCompiledClass stubCompiledCode = retrieveStubCode();
 145 
 146         // Free memory!
 147         try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
 148             main.printMemoryUsage();
 149             System.gc();
 150         }
 151 
 152         MetadataBuilder metadataBuilder = null;
 153         try (Timer t = new Timer(main, "Processing metadata")) {
 154             /*
 155              * Generate metadata for compiled code and copy it into metadata section. Create
 156              * relocation information for all references (call, constants, etc) in compiled code.
 157              */
 158             metadataBuilder = new MetadataBuilder(this);
 159             metadataBuilder.processMetadata(classes, stubCompiledCode);
 160         }
 161 
 162         // Free memory!
 163         try (Timer t = main.options.verbose ? new Timer(main, "Freeing memory") : null) {
 164             main.printMemoryUsage();
 165             System.gc();
 166         }
 167 
 168         try (Timer t = new Timer(main, "Preparing stubs binary")) {
 169             prepareStubsBinary(stubCompiledCode);
 170         }
 171         try (Timer t = new Timer(main, "Preparing compiled binary")) {
 172             // Should be called after Stubs because they can set dependent klasses.
 173             prepareCompiledBinary(metadataBuilder);
 174         }
 175     }
 176 
 177     /**
 178      * Get all stubs from Graal and add them to the code section.
 179      */
 180     @SuppressWarnings("try")
 181     private AOTCompiledClass retrieveStubCode() {
 182         ArrayList<CompiledMethodInfo> stubs = new ArrayList<>();
 183         for (Stub stub : Stub.getStubs()) {
 184             try (Scope scope = Debug.scope("CompileStubs")) {
 185                 CompilationResult result = stub.getCompilationResult(backend);
 186                 CompiledMethodInfo cm = new CompiledMethodInfo(result, new AOTStub(stub, backend));
 187                 stubs.add(cm);
 188             } catch (Throwable e) {
 189                 throw Debug.handle(e);
 190             }
 191         }
 192         AOTCompiledClass stubCompiledCode = new AOTCompiledClass(stubs);
 193         CodeSectionProcessor codeSectionProcessor = new CodeSectionProcessor(this);
 194         codeSectionProcessor.process(stubCompiledCode);
 195         return stubCompiledCode;
 196     }
 197 
 198     /**
 199      * Prepare metaspace.offsets section.
 200      */
 201     private void prepareCompiledBinary(MetadataBuilder metadataBuilder) {
 202         for (AOTCompiledClass c : classes) {
 203             // Create records for compiled AOT methods.
 204             c.putMethodsData(binaryContainer);
 205         }
 206         // Create records for compiled AOT classes.
 207         AOTCompiledClass.putAOTKlassData(binaryContainer);
 208 
 209         // Fill in AOTHeader
 210         HeaderContainer header = binaryContainer.getHeaderContainer();
 211         header.setClassesCount(AOTCompiledClass.getClassesCount());
 212         header.setMethodsCount(CompiledMethodInfo.getMethodsCount());
 213         // Record size of got sections
 214         ByteContainer bc = binaryContainer.getMetaspaceGotContainer();
 215         header.setMetaspaceGotSize((bc.getByteStreamSize() / 8));
 216         bc = binaryContainer.getMetadataGotContainer();
 217         header.setMetadataGotSize((bc.getByteStreamSize() / 8));
 218         bc = binaryContainer.getOopGotContainer();
 219         header.setOopGotSize((bc.getByteStreamSize() / 8));
 220     }
 221 
 222     /**
 223      * Prepare stubs.offsets section.
 224      */
 225     private void prepareStubsBinary(AOTCompiledClass compiledClass) {
 226         // For each of the compiled stubs, create records holding information about
 227         // them.
 228         ArrayList<CompiledMethodInfo> compiledStubs = compiledClass.getCompiledMethods();
 229         int cntStubs = compiledStubs.size();
 230         binaryContainer.addMethodsCount(cntStubs, binaryContainer.getStubsOffsetsContainer());
 231         for (CompiledMethodInfo methodInfo : compiledStubs) {
 232             // Note, stubs have different offsets container.
 233             methodInfo.addMethodOffsets(binaryContainer, binaryContainer.getStubsOffsetsContainer());
 234         }
 235     }
 236 
 237 }