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.Map; 29 30import jdk.tools.jaotc.binformat.BinaryContainer; 31import jdk.tools.jaotc.binformat.ReadOnlyDataContainer; 32import jdk.tools.jaotc.binformat.Symbol.Binding; 33import jdk.tools.jaotc.binformat.Symbol.Kind; 34 35import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; 36import jdk.vm.ci.meta.ResolvedJavaField; 37import jdk.vm.ci.meta.ResolvedJavaMethod; 38import jdk.vm.ci.meta.ResolvedJavaType; 39 40/** 41 * Class encapsulating Graal-compiled output of a Java class. The compilation result of all methods 42 * of a class {@code className} are maintained in an array list. 43 */ 44final class AOTCompiledClass { 45 46 static class AOTKlassData { 47 private int gotIndex; // Index (offset/8) to the got in the .metaspace.got section 48 private int classId; // Unique ID 49 // Offset to compiled methods data in the .methods.offsets section. 50 private int compiledMethodsOffset; 51 // Offset to dependent methods data. 52 private int dependentMethodsOffset; 53 private long fingerprint; // Class fingerprint 54 55 private final String name; 56 private boolean isArray; 57 58 /** 59 * List of dependent compiled methods which have a reference to this class. 60 */ 61 private ArrayList<CompiledMethodInfo> dependentMethods; 62 63 AOTKlassData(BinaryContainer binaryContainer, String name, long fingerprint, int classId) { 64 this.dependentMethods = new ArrayList<>(); 65 this.classId = classId; 66 this.fingerprint = fingerprint; 67 this.gotIndex = binaryContainer.addTwoSlotKlassSymbol(name); 68 this.compiledMethodsOffset = -1; // Not compiled classes do not have compiled methods. 69 this.dependentMethodsOffset = -1; 70 this.name = name; 71 this.isArray = name.length() > 0 && name.charAt(0) == '['; 72 } 73 74 long getFingerprint() { 75 return fingerprint; 76 } 77 78 /** 79 * Add a method to the list of dependent methods. 80 */ 81 synchronized boolean addDependentMethod(CompiledMethodInfo cm) { 82 return dependentMethods.add(cm); 83 } 84 85 /** 86 * Return the array list of dependent class methods. 87 * 88 * @return array list of dependent methods 89 */ 90 ArrayList<CompiledMethodInfo> getDependentMethods() { 91 return dependentMethods; 92 } 93 94 /** 95 * Returns if this class has dependent methods. 96 * 97 * @return true if dependent methods exist, false otherwise 98 */ 99 boolean hasDependentMethods() { 100 return !dependentMethods.isEmpty(); 101 } 102 103 void setCompiledMethodsOffset(int offset) { 104 compiledMethodsOffset = offset; 105 } 106 107 protected void putAOTKlassData(BinaryContainer binaryContainer, ReadOnlyDataContainer container) { 108 int cntDepMethods = dependentMethods.size(); 109 // Create array of dependent methods IDs. First word is count. 110 ReadOnlyDataContainer dependenciesContainer = binaryContainer.getKlassesDependenciesContainer(); 111 this.dependentMethodsOffset = BinaryContainer.addMethodsCount(cntDepMethods, dependenciesContainer); 112 for (CompiledMethodInfo methodInfo : dependentMethods) { 113 dependenciesContainer.appendInt(methodInfo.getCodeId()); 114 } 115 verify(); 116 117 // @formatter:off 118 /* 119 * The offsets layout should match AOTKlassData structure in AOT JVM runtime 120 */ 121 int offset = container.getByteStreamSize(); 122 container.createSymbol(offset, Kind.OBJECT, Binding.GLOBAL, 0, name); 123 // Add index (offset/8) to the got in the .metaspace.got section 124 container.appendInt(gotIndex). 125 // Add unique ID 126 appendInt(classId). 127 // Add the offset to compiled methods data in the .metaspace.offsets section. 128 appendInt(compiledMethodsOffset). 129 // Add the offset to dependent methods data in the .metaspace.offsets section. 130 appendInt(dependentMethodsOffset). 131 // Add fingerprint. 132 appendLong(fingerprint); 133 // @formatter:on 134 } 135 136 private void verify() { 137 assert gotIndex > 0 : "incorrect gotIndex: " + gotIndex + " for klass: " + name; 138 assert isArray || fingerprint != 0 : "incorrect fingerprint: " + fingerprint + " for klass: " + name; 139 assert compiledMethodsOffset >= -1 : "incorrect compiledMethodsOffset: " + compiledMethodsOffset + " for klass: " + name; 140 assert dependentMethodsOffset >= -1 : "incorrect dependentMethodsOffset: " + dependentMethodsOffset + " for klass: " + name; 141 assert classId >= 0 : "incorrect classId: " + classId + " for klass: " + name; 142 } 143 144 } 145 146 private final HotSpotResolvedObjectType resolvedJavaType; 147 148 /** 149 * List of all collected class data. 150 */ 151 private static Map<String, AOTKlassData> klassData = new HashMap<>(); 152 153 /** 154 * List of all methods to be compiled. 155 */ 156 private ArrayList<ResolvedJavaMethod> methods = new ArrayList<>(); 157 158 /** 159 * List of all compiled class methods. 160 */ 161 private ArrayList<CompiledMethodInfo> compiledMethods; 162 163 /** 164 * If this class represents Graal stub code. 165 */ 166 private final boolean representsStubs; 167 168 /** 169 * Classes count used to generate unique global method id. 170 */ 171 private static int classesCount = 0; 172 173 /** 174 * Construct an object with compiled methods. Intended to be used for code with no corresponding 175 * Java method name in the user application. 176 * 177 * @param compiledMethods AOT compiled methods 178 */ 179 AOTCompiledClass(ArrayList<CompiledMethodInfo> compiledMethods) { 180 this.resolvedJavaType = null; 181 this.compiledMethods = compiledMethods; 182 this.representsStubs = true; 183 } 184 185 /** 186 * Construct an object with compiled versions of the named class. 187 */ 188 AOTCompiledClass(ResolvedJavaType resolvedJavaType) { 189 this.resolvedJavaType = (HotSpotResolvedObjectType) resolvedJavaType; 190 this.compiledMethods = new ArrayList<>(); 191 this.representsStubs = false; 192 } 193 194 /** 195 * @return the ResolvedJavaType of this class 196 */ 197 ResolvedJavaType getResolvedJavaType() { 198 return resolvedJavaType; 199 } 200 201 /** 202 * Get the list of methods which should be compiled. 203 */ 204 ArrayList<ResolvedJavaMethod> getMethods() { 205 ArrayList<ResolvedJavaMethod> m = methods; 206 methods = null; // Free - it is not used after that. 207 return m; 208 } 209 210 /** 211 * Get the number of all AOT classes. 212 */ 213 static int getClassesCount() { 214 return classesCount; 215 } 216 217 /** 218 * Get the number of methods which should be compiled. 219 * 220 * @return number of methods which should be compiled 221 */ 222 int getMethodCount() { 223 return methods.size(); 224 } 225 226 /** 227 * Add a method to the list of methods to be compiled. 228 */ 229 void addMethod(ResolvedJavaMethod method) { 230 methods.add(method); 231 } 232 233 /** 234 * Returns if this class has methods which should be compiled. 235 * 236 * @return true if this class contains methods which should be compiled, false otherwise 237 */ 238 boolean hasMethods() { 239 return !methods.isEmpty(); 240 } 241 242 /** 243 * Add a method to the list of compiled methods. This method needs to be thread-safe. 244 */ 245 synchronized boolean addCompiledMethod(CompiledMethodInfo cm) { 246 return compiledMethods.add(cm); 247 } 248 249 /** 250 * Return the array list of compiled class methods. 251 * 252 * @return array list of compiled methods 253 */ 254 ArrayList<CompiledMethodInfo> getCompiledMethods() { 255 return compiledMethods; 256 } 257 258 /** 259 * Returns if this class has successfully compiled methods. 260 * 261 * @return true if methods were compiled, false otherwise 262 */ 263 boolean hasCompiledMethods() { 264 return !compiledMethods.isEmpty(); 265 } 266 267 /** 268 * Add a klass data. 269 */ 270 synchronized static AOTKlassData addAOTKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) { 271 String name = type.getName(); 272 long fingerprint = type.getFingerprint(); 273 AOTKlassData data = klassData.get(name); 274 if (data != null) { 275 assert data.getFingerprint() == fingerprint : "incorrect fingerprint data for klass: " + name; 276 } else { 277 data = new AOTKlassData(binaryContainer, name, fingerprint, classesCount++); 278 klassData.put(name, data); 279 } 280 return data; 281 } 282 283 synchronized static AOTKlassData getAOTKlassData(String name) { 284 return klassData.get(name); 285 } 286 287 synchronized static AOTKlassData getAOTKlassData(HotSpotResolvedObjectType type) { 288 return getAOTKlassData(type.getName()); 289 } 290 291 void addAOTKlassData(BinaryContainer binaryContainer) { 292 for (CompiledMethodInfo methodInfo : compiledMethods) { 293 // Record methods holder 294 methodInfo.addDependentKlassData(binaryContainer, resolvedJavaType); 295 // Record inlinee classes 296 ResolvedJavaMethod[] inlinees = methodInfo.getCompilationResult().getMethods(); 297 if (inlinees != null) { 298 for (ResolvedJavaMethod m : inlinees) { 299 methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) m.getDeclaringClass()); 300 } 301 } 302 // Record classes of fields that were accessed 303 ResolvedJavaField[] fields = methodInfo.getCompilationResult().getFields(); 304 if (fields != null) { 305 for (ResolvedJavaField f : fields) { 306 methodInfo.addDependentKlassData(binaryContainer, (HotSpotResolvedObjectType) f.getDeclaringClass()); 307 } 308 } 309 } 310 } 311 312 synchronized static AOTKlassData addFingerprintKlassData(BinaryContainer binaryContainer, HotSpotResolvedObjectType type) { 313 if (type.isArray()) { 314 return addAOTKlassData(binaryContainer, type); 315 } 316 assert type.getFingerprint() != 0 : "no fingerprint for " + type.getName(); 317 AOTKlassData old = getAOTKlassData(type); 318 if (old != null) { 319 boolean assertsEnabled = false; 320 // Next assignment will be executed when asserts are enabled. 321 assert assertsEnabled = true; 322 if (assertsEnabled) { 323 HotSpotResolvedObjectType s = type.getSuperclass(); 324 if (s != null) { 325 assert getAOTKlassData(s) != null : "fingerprint for super " + s.getName() + " needed for " + type.getName(); 326 } 327 for (HotSpotResolvedObjectType i : type.getInterfaces()) { 328 assert getAOTKlassData(i) != null : "fingerprint for interface " + i.getName() + " needed for " + type.getName(); 329 } 330 } 331 return old; 332 } 333 334 // Fingerprinting requires super classes and super interfaces 335 HotSpotResolvedObjectType s = type.getSuperclass(); 336 if (s != null) { 337 addFingerprintKlassData(binaryContainer, s); 338 } 339 for (HotSpotResolvedObjectType i : type.getInterfaces()) { 340 addFingerprintKlassData(binaryContainer, i); 341 } 342 343 return addAOTKlassData(binaryContainer, type); 344 } 345 346 /* 347 * Put methods data to contained. 348 */ 349 void putMethodsData(BinaryContainer binaryContainer) { 350 ReadOnlyDataContainer container = binaryContainer.getMethodsOffsetsContainer(); 351 int cntMethods = compiledMethods.size(); 352 int startMethods = BinaryContainer.addMethodsCount(cntMethods, container); 353 for (CompiledMethodInfo methodInfo : compiledMethods) { 354 methodInfo.addMethodOffsets(binaryContainer, container); 355 } 356 String name = resolvedJavaType.getName(); 357 AOTKlassData data = klassData.get(name); 358 assert data != null : "missing data for klass: " + name; 359 assert data.getFingerprint() == resolvedJavaType.getFingerprint() : "incorrect fingerprint for klass: " + name; 360 int cntDepMethods = data.dependentMethods.size(); 361 assert cntDepMethods > 0 : "no dependent methods for compiled klass: " + name; 362 data.setCompiledMethodsOffset(startMethods); 363 } 364 365 static void putAOTKlassData(BinaryContainer binaryContainer) { 366 ReadOnlyDataContainer container = binaryContainer.getKlassesOffsetsContainer(); 367 for (AOTKlassData data : klassData.values()) { 368 data.putAOTKlassData(binaryContainer, container); 369 } 370 } 371 372 boolean representsStubs() { 373 return representsStubs; 374 } 375 376 void clear() { 377 for (CompiledMethodInfo c : compiledMethods) { 378 c.clear(); 379 } 380 this.compiledMethods = null; 381 this.methods = null; 382 } 383 384} 385