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 
  24 package jdk.tools.jaotc;
  25 
  26 import java.util.ArrayList;
  27 import java.util.HashMap;
  28 import java.util.Set;
  29 
  30 import jdk.tools.jaotc.binformat.BinaryContainer;
  31 import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
  32 import jdk.tools.jaotc.binformat.Symbol.Binding;
  33 import jdk.tools.jaotc.binformat.Symbol.Kind;
  34 
  35 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
  36 import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
  37 import jdk.vm.ci.meta.ResolvedJavaField;
  38 import jdk.vm.ci.meta.ResolvedJavaMethod;
  39 import jdk.vm.ci.meta.ResolvedJavaType;
  40 
  41 import jdk.tools.jaotc.AOTDynamicTypeStore.AdapterLocation;
  42 import jdk.tools.jaotc.AOTDynamicTypeStore.AppendixLocation;
  43 import jdk.tools.jaotc.AOTDynamicTypeStore.Location;
  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 
  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 
  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 
  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<String>();
  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 
 116         HotSpotResolvedObjectType getType() {
 117             return type;
 118         }
 119 
 120         String getMetadataName() {
 121             return metadataName;
 122         }
 123 
 124         /**
 125          * Add a method to the list of dependent methods.
 126          */
 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          */
 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          */
 145         boolean hasDependentMethods() {
 146             return !dependentMethods.isEmpty();
 147         }
 148 
 149         void setCompiledMethodsOffset(int offset) {
 150             compiledMethodsOffset = offset;
 151         }
 152 
 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 
 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      */
 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      */
 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      */
 249     ResolvedJavaType getResolvedJavaType() {
 250         return resolvedJavaType;
 251     }
 252 
 253     /**
 254      * Get the list of methods which should be compiled.
 255      */
 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      */
 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      */
 274     int getMethodCount() {
 275         return methods.size();
 276     }
 277 
 278     /**
 279      * Add a method to the list of methods to be compiled.
 280      */
 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      */
 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      */
 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      */
 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      */
 315     boolean hasCompiledMethods() {
 316         return !compiledMethods.isEmpty();
 317     }
 318 
 319     /**
 320      * Add a klass data.
 321      */
 322     synchronized static AOTKlassData addAOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) {
 323         String name = type.getName();
 324         AOTKlassData data = klassData.get(name);
 325         if (data != null) {
 326             assert data.getType() == type : "duplicate classes for name " + name;
 327         } else {
 328             data = new AOTKlassData(binaryContainer, type, classesCount++);
 329             klassData.put(name, data);
 330         }
 331         return data;
 332     }
 333 
 334     private synchronized static AOTKlassData getAOTKlassData(String name) {
 335         return klassData.get(name);
 336     }
 337 
 338     synchronized static AOTKlassData getAOTKlassData(HotSpotResolvedObjectType type) {
 339         String name = type.getName();
 340         AOTKlassData data =  getAOTKlassData(name);
 341         assert data == null || data.getType() == type : "duplicate classes for name " + name;
 342         return data;
 343     }
 344 
 345     void addAOTKlassData(BinaryContainer binaryContainer) {
 346         for (CompiledMethodInfo methodInfo : compiledMethods) {
 347             // Record methods holder
 348             methodInfo.addDependentKlassData(binaryContainer, resolvedJavaType);
 349             // Record inlinee classes
 350             ResolvedJavaMethod[] inlinees = methodInfo.getCompilationResult().getMethods();
 351             if (inlinees != null) {
 352                 for (ResolvedJavaMethod m : inlinees) {
 353                     methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) m.getDeclaringClass());
 354                 }
 355             }
 356             // Record classes of fields that were accessed
 357             ResolvedJavaField[] fields = methodInfo.getCompilationResult().getFields();
 358             if (fields != null) {
 359                 for (ResolvedJavaField f : fields) {
 360                     methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) f.getDeclaringClass());
 361                 }
 362             }
 363         }
 364     }
 365 
 366     synchronized static AOTKlassData addFingerprintKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) {
 367         if (type.isArray()) {
 368             return addAOTKlassData(binaryContainer, type);
 369         }
 370         assert type.getFingerprint() != 0 : "no fingerprint for " + type.getName();
 371         AOTKlassData old = getAOTKlassData(type);
 372         if (old != null) {
 373             boolean assertsEnabled = false;
 374             // Next assignment will be executed when asserts are enabled.
 375             assert assertsEnabled = true;
 376             if (assertsEnabled) {
 377                 HotSpotResolvedObjectType s = type.getSuperclass();
 378                 if (s != null) {
 379                     assert getAOTKlassData(s) != null : "fingerprint for super " + s.getName() + " needed for " + type.getName();
 380                 }
 381                 for (HotSpotResolvedObjectType i : type.getInterfaces()) {
 382                     assert getAOTKlassData(i) != null : "fingerprint for interface " + i.getName() + " needed for " + type.getName();
 383                 }
 384             }
 385             return old;
 386         }
 387 
 388         // Fingerprinting requires super classes and super interfaces
 389         HotSpotResolvedObjectType s = type.getSuperclass();
 390         if (s != null) {
 391             addFingerprintKlassData(binaryContainer, s);
 392         }
 393         for (HotSpotResolvedObjectType i : type.getInterfaces()) {
 394             addFingerprintKlassData(binaryContainer, i);
 395         }
 396 
 397         return addAOTKlassData(binaryContainer, type);
 398     }
 399 
 400     /*
 401      * Put methods data to contained.
 402      */
 403     void putMethodsData(BinaryContainer binaryContainer) {
 404         ReadOnlyDataContainer container = binaryContainer.getMethodsOffsetsContainer();
 405         int cntMethods = compiledMethods.size();
 406         int startMethods = BinaryContainer.addMethodsCount(cntMethods, container);
 407         for (CompiledMethodInfo methodInfo : compiledMethods) {
 408             methodInfo.addMethodOffsets(binaryContainer, container);
 409         }
 410         String name = resolvedJavaType.getName();
 411         AOTKlassData data = getAOTKlassData(resolvedJavaType);
 412         assert data != null : "missing data for klass: " + name;
 413         int cntDepMethods = data.dependentMethods.size();
 414         assert cntDepMethods > 0 : "no dependent methods for compiled klass: " + name;
 415         data.setCompiledMethodsOffset(startMethods);
 416     }
 417 
 418     static void putAOTKlassData(BinaryContainer binaryContainer) {
 419         // record dynamic types
 420         Set<HotSpotResolvedObjectType> dynoTypes = dynoStore.getDynamicTypes();
 421         if (dynoTypes != null) {
 422             for (HotSpotResolvedObjectType dynoType : dynoTypes) {
 423                 addFingerprintKlassData(binaryContainer, dynoType);
 424             }
 425         }
 426 
 427         ReadOnlyDataContainer container = binaryContainer.getKlassesOffsetsContainer();
 428         for (AOTKlassData data : klassData.values()) {
 429             data.putAOTKlassData(binaryContainer, container);
 430         }
 431     }
 432 
 433     static HotSpotResolvedObjectType getType(Object ref) {
 434         return (ref instanceof HotSpotResolvedObjectType) ?
 435             (HotSpotResolvedObjectType)ref :
 436             ((HotSpotResolvedJavaMethod)ref).getDeclaringClass();
 437     }
 438 
 439     static String metadataName(HotSpotResolvedObjectType type) {
 440         AOTKlassData data = getAOTKlassData(type);
 441         assert data != null : "no data for " + type;
 442         return getAOTKlassData(type).getMetadataName();
 443     }
 444 
 445     private static String metadataName(HotSpotResolvedJavaMethod m) {
 446         return metadataName(m.getDeclaringClass()) + "." + m.getName() + m.getSignature().toMethodDescriptor();
 447     }
 448 
 449     static String metadataName(Object ref) {
 450         if (ref instanceof HotSpotResolvedJavaMethod) {
 451             HotSpotResolvedJavaMethod m = (HotSpotResolvedJavaMethod)ref;
 452             return metadataName(m);
 453         } else {
 454             assert ref instanceof HotSpotResolvedObjectType : "unexpected object type " + ref.getClass().getName();
 455             HotSpotResolvedObjectType type = (HotSpotResolvedObjectType)ref;
 456             return metadataName(type);
 457         }
 458     }
 459 
 460     boolean representsStubs() {
 461         return representsStubs;
 462     }
 463 
 464     void clear() {
 465         for (CompiledMethodInfo c : compiledMethods) {
 466             c.clear();
 467         }
 468         this.compiledMethods = null;
 469         this.methods = null;
 470     }
 471 
 472 }