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