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 try { 443 AOTKlassData t = getAOTKlassData(type); 444 t.getMetadataName(); 445 } catch (NullPointerException e) { 446 return type.getName(); 447 } 448 return getAOTKlassData(type).getMetadataName(); 449 } 450 451 private static String metadataName(HotSpotResolvedJavaMethod m) { 452 return metadataName(m.getDeclaringClass()) + "." + m.getName() + m.getSignature().toMethodDescriptor(); 453 } 454 455 static String metadataName(Object ref) { 456 if (ref instanceof HotSpotResolvedJavaMethod) { 457 HotSpotResolvedJavaMethod m = (HotSpotResolvedJavaMethod)ref; 458 return metadataName(m); 459 } else { 460 assert ref instanceof HotSpotResolvedObjectType : "unexpected object type " + ref.getClass().getName(); 461 HotSpotResolvedObjectType type = (HotSpotResolvedObjectType)ref; 462 return metadataName(type); 463 } 464 } 465 466 boolean representsStubs() { 467 return representsStubs; 468 } 469 470 void clear() { 471 for (CompiledMethodInfo c : compiledMethods) { 472 c.clear(); 473 } 474 this.compiledMethods = null; 475 this.methods = null; 476 } 477 478 }