1 /* 2 * Copyright (c) 2016, 2019, 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 static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; 29 import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode; 30 import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT; 31 32 import java.io.IOException; 33 import java.io.PrintWriter; 34 import java.text.MessageFormat; 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.List; 38 import java.util.ListIterator; 39 import java.util.Set; 40 import java.util.StringTokenizer; 41 import java.util.stream.Stream; 42 import java.nio.file.Files; 43 import java.nio.file.Paths; 44 45 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 46 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler; 47 import org.graalvm.compiler.debug.DebugContext; 48 import org.graalvm.compiler.debug.DebugContext.Activation; 49 import org.graalvm.compiler.hotspot.CompilerConfigurationFactory; 50 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 51 import org.graalvm.compiler.hotspot.HotSpotGraalCompilerFactory; 52 import org.graalvm.compiler.hotspot.HotSpotGraalOptionValues; 53 import org.graalvm.compiler.hotspot.HotSpotGraalRuntime; 54 import org.graalvm.compiler.hotspot.HotSpotGraalRuntime.HotSpotGC; 55 import org.graalvm.compiler.hotspot.HotSpotHostBackend; 56 import org.graalvm.compiler.hotspot.meta.HotSpotInvokeDynamicPlugin; 57 import org.graalvm.compiler.java.GraphBuilderPhase; 58 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 59 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; 60 import org.graalvm.compiler.options.OptionValues; 61 import org.graalvm.compiler.phases.BasePhase; 62 import org.graalvm.compiler.phases.PhaseSuite; 63 import org.graalvm.compiler.phases.tiers.HighTierContext; 64 import org.graalvm.compiler.printer.GraalDebugHandlersFactory; 65 import org.graalvm.compiler.runtime.RuntimeProvider; 66 67 import jdk.tools.jaotc.Options.Option; 68 import jdk.tools.jaotc.binformat.BinaryContainer; 69 import jdk.vm.ci.meta.MetaAccessProvider; 70 import jdk.vm.ci.meta.ResolvedJavaMethod; 71 import jdk.vm.ci.runtime.JVMCI; 72 73 public final class Main { 74 75 final Options options = new Options(); 76 private PrintWriter log; 77 LogPrinter printer; 78 GraalFilters filters; 79 80 private static final int EXIT_OK = 0; // No errors. 81 private static final int EXIT_CMDERR = 2; // Bad command-line arguments and/or switches. 82 private static final int EXIT_ABNORMAL = 4; // Terminated abnormally. 83 84 private static final String PROGNAME = "jaotc"; 85 86 private static final String JVM_VERSION = System.getProperty("java.runtime.version"); 87 main(String[] args)88 public static void main(String[] args) throws Exception { 89 Main t = new Main(); 90 final int exitCode = t.run(parse(args)); 91 System.exit(exitCode); 92 } 93 94 /** 95 * Expands '@file' in command line arguments by replacing '@file' with the content of 'file' 96 * parsed by StringTokenizer. '@' character can be quoted as '@@'. 97 */ parse(String[] args)98 private static String[] parse(String[] args) throws IOException { 99 List<String> result = new ArrayList<>(); 100 for (String arg : args) { 101 if (arg.length() > 1 && arg.charAt(0) == '@') { 102 String v = arg.substring(1); 103 if (v.charAt(0) == '@') { 104 result.add(v); 105 } else { 106 try (Stream<String> file = Files.lines(Paths.get(v))) { 107 file.map(StringTokenizer::new).map(Collections::list).flatMap(l -> l.stream().map(o -> (String) o)).forEachOrdered(result::add); 108 } 109 } 110 } else { 111 result.add(arg); 112 } 113 } 114 return result.toArray(String[]::new); 115 } 116 run(String[] args)117 private int run(String[] args) { 118 log = new PrintWriter(System.out); 119 printer = new LogPrinter(this, log); 120 121 try { 122 Options.handleOptions(this, args); 123 if (options.help) { 124 showHelp(); 125 return EXIT_OK; 126 } 127 if (options.version) { 128 showVersion(); 129 return EXIT_OK; 130 } 131 132 printer.printlnInfo("Compiling " + options.outputName + "..."); 133 final long start = System.currentTimeMillis(); 134 if (!run()) { 135 return EXIT_ABNORMAL; 136 } 137 final long end = System.currentTimeMillis(); 138 printer.printlnInfo("Total time: " + (end - start) + " ms"); 139 140 return EXIT_OK; 141 } catch (Options.BadArgs e) { 142 printer.reportError(e.key, e.args); 143 if (e.showUsage) { 144 showUsage(); 145 } 146 return EXIT_CMDERR; 147 } catch (Exception e) { 148 e.printStackTrace(); 149 return EXIT_ABNORMAL; 150 } finally { 151 log.flush(); 152 } 153 } 154 155 @SuppressWarnings("try") run()156 private boolean run() throws Exception { 157 LogPrinter.openLog(); 158 159 try { 160 161 final Linker linker = new Linker(this); 162 final String objectFileName = linker.objFile(); 163 final Collector collector = new Collector(this); 164 Set<Class<?>> classesToCompile; 165 166 try (Timer t = new Timer(this, "")) { 167 classesToCompile = collector.collectClassesToCompile(); 168 printer.printInfo(classesToCompile.size() + " classes found"); 169 } 170 171 OptionValues graalOptions = HotSpotGraalOptionValues.defaultOptions(); 172 // Setting -Dgraal.TieredAOT overrides --compile-for-tiered 173 if (!TieredAOT.hasBeenSet(graalOptions)) { 174 graalOptions = new OptionValues(graalOptions, TieredAOT, options.tiered); 175 } 176 graalOptions = new OptionValues(graalOptions, GeneratePIC, true, ImmutableCode, true); 177 GraalJVMCICompiler graalCompiler = HotSpotGraalCompilerFactory.createCompiler("JAOTC", JVMCI.getRuntime(), graalOptions, CompilerConfigurationFactory.selectFactory(null, graalOptions)); 178 HotSpotGraalRuntime runtime = (HotSpotGraalRuntime) graalCompiler.getGraalRuntime(); 179 HotSpotHostBackend backend = (HotSpotHostBackend) runtime.getCapability(RuntimeProvider.class).getHostBackend(); 180 MetaAccessProvider metaAccess = backend.getProviders().getMetaAccess(); 181 filters = new GraalFilters(metaAccess); 182 183 List<AOTCompiledClass> classes; 184 185 try (Timer t = new Timer(this, "")) { 186 classes = collector.collectMethodsToCompile(classesToCompile, metaAccess); 187 } 188 189 // Free memory! 190 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 191 printer.printMemoryUsage(); 192 classesToCompile = null; 193 System.gc(); 194 } 195 196 AOTDynamicTypeStore dynoStore = new AOTDynamicTypeStore(); 197 AOTCompiledClass.setDynamicTypeStore(dynoStore); 198 199 // AOTBackend aotBackend = new AOTBackend(this, graalOptions, backend, new 200 // HotSpotInvokeDynamicPlugin(dynoStore)); 201 // Temporary workaround until JDK-8223533 is fixed. 202 // Disable invokedynamic support. 203 var indyPlugin = new HotSpotInvokeDynamicPlugin(dynoStore) { 204 @Override 205 public boolean supportsDynamicInvoke(GraphBuilderContext builder, int index, int opcode) { 206 return false; 207 } 208 }; 209 AOTBackend aotBackend = new AOTBackend(this, graalOptions, backend, indyPlugin); 210 SnippetReflectionProvider snippetReflection = aotBackend.getProviders().getSnippetReflection(); 211 AOTCompiler compiler = new AOTCompiler(this, graalOptions, aotBackend, options.threads); 212 classes = compiler.compileClasses(classes); 213 214 GraalHotSpotVMConfig graalHotSpotVMConfig = runtime.getVMConfig(); 215 PhaseSuite<HighTierContext> graphBuilderSuite = aotBackend.getGraphBuilderSuite(); 216 ListIterator<BasePhase<? super HighTierContext>> iterator = graphBuilderSuite.findPhase(GraphBuilderPhase.class); 217 GraphBuilderConfiguration graphBuilderConfig = ((GraphBuilderPhase) iterator.previous()).getGraphBuilderConfig(); 218 219 // Free memory! 220 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 221 printer.printMemoryUsage(); 222 aotBackend = null; 223 compiler = null; 224 System.gc(); 225 } 226 227 HotSpotGC graalGC = runtime.getGarbageCollector(); 228 // Prior to JDK 14, the Graal HotSpotGC enum order matched the JDK CollectedHeap enum 229 // order, so using the ordinal value worked fine. In JDK 14, CMS was removed on the 230 // JDK side, so we need a symbolic lookup of the JDK value. 231 int def = graalGC.ordinal() + 1; 232 // The GC names are spelled the same in both enums, so no clever remapping is needed 233 // here. 234 String name = "CollectedHeap::" + graalGC.name(); 235 int gc = graalHotSpotVMConfig.getConstant(name, Integer.class, def); 236 237 BinaryContainer binaryContainer = new BinaryContainer(graalOptions, graalHotSpotVMConfig, graphBuilderConfig, gc, JVM_VERSION); 238 DataBuilder dataBuilder = new DataBuilder(this, backend, classes, binaryContainer); 239 240 try (DebugContext debug = DebugContext.create(graalOptions, new GraalDebugHandlersFactory(snippetReflection)); Activation a = debug.activate()) { 241 dataBuilder.prepareData(debug); 242 } 243 244 // Print information about section sizes 245 printer.containersInfo(binaryContainer); 246 247 // Free memory! 248 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 249 printer.printMemoryUsage(); 250 backend = null; 251 for (AOTCompiledClass aotCompClass : classes) { 252 aotCompClass.clear(); 253 } 254 classes.clear(); 255 classes = null; 256 dataBuilder = null; 257 binaryContainer.freeMemory(); 258 System.gc(); 259 } 260 261 try (Timer t = new Timer(this, "Creating binary: " + objectFileName)) { 262 binaryContainer.createBinary(objectFileName); 263 } 264 265 // Free memory! 266 try (Timer t = options.verbose ? new Timer(this, "Freeing memory") : null) { 267 printer.printMemoryUsage(); 268 binaryContainer = null; 269 System.gc(); 270 } 271 272 try (Timer t = new Timer(this, "Creating shared library: " + linker.libFile())) { 273 linker.link(); 274 } 275 276 printer.printVerbose("Final memory "); 277 printer.printMemoryUsage(); 278 printer.printlnVerbose(""); 279 280 } finally { 281 LogPrinter.closeLog(); 282 } 283 return true; 284 } 285 handleError(ResolvedJavaMethod resolvedMethod, Throwable e, String message)286 void handleError(ResolvedJavaMethod resolvedMethod, Throwable e, String message) { 287 String methodName = JavaMethodInfo.uniqueMethodName(resolvedMethod); 288 289 if (options.debug) { 290 printer.printError("Failed compilation: " + methodName + ": " + e); 291 } 292 293 // Ignore some exceptions when meta-compiling Graal. 294 if (GraalFilters.shouldIgnoreException(e)) { 295 return; 296 } 297 298 LogPrinter.writeLog("Failed compilation of method " + methodName + message); 299 300 if (!options.debug) { 301 printer.printError("Failed compilation: " + methodName + ": " + e); 302 } 303 304 if (options.verbose) { 305 e.printStackTrace(log); 306 } 307 308 if (options.exitOnError) { 309 System.exit(1); 310 } 311 } 312 warning(String key, Object... args)313 void warning(String key, Object... args) { 314 log.println("Warning: " + MessageFormat.format(key, args)); 315 log.flush(); 316 } 317 showUsage()318 private void showUsage() { 319 log.println("Usage: " + PROGNAME + " <options> list"); 320 log.println("use --help for a list of possible options"); 321 log.flush(); 322 } 323 showHelp()324 private void showHelp() { 325 log.println("Usage: " + PROGNAME + " <options> list"); 326 log.println(); 327 log.println(" list A : separated list of class names, modules, jar files"); 328 log.println(" or directories which contain class files."); 329 log.println(); 330 log.println("where options include:"); 331 for (Option o : Options.recognizedOptions) { 332 String name = o.aliases[0].substring(1); // there must always be at least one name 333 name = name.charAt(0) == '-' ? name.substring(1) : name; 334 if (o.isHidden() || name.equals("h")) { 335 continue; 336 } 337 log.println(o.help); 338 } 339 log.flush(); 340 } 341 showVersion()342 private void showVersion() { 343 log.println(PROGNAME + " " + JVM_VERSION); 344 } 345 } 346