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 package org.graalvm.compiler.hotspot;
26 
27 import static jdk.vm.ci.common.InitTimer.timer;
28 
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.stream.Collectors;
34 
35 import jdk.internal.vm.compiler.collections.EconomicMap;
36 import org.graalvm.compiler.core.common.SuppressFBWarnings;
37 import org.graalvm.compiler.debug.GraalError;
38 import org.graalvm.compiler.debug.TTY;
39 import org.graalvm.compiler.lir.phases.LIRPhase;
40 import org.graalvm.compiler.lir.phases.LIRPhaseSuite;
41 import org.graalvm.compiler.options.EnumOptionKey;
42 import org.graalvm.compiler.options.Option;
43 import org.graalvm.compiler.options.OptionKey;
44 import org.graalvm.compiler.options.OptionStability;
45 import org.graalvm.compiler.options.OptionType;
46 import org.graalvm.compiler.options.OptionValues;
47 import org.graalvm.compiler.phases.BasePhase;
48 import org.graalvm.compiler.phases.PhaseSuite;
49 import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
50 import org.graalvm.compiler.serviceprovider.GraalServices;
51 
52 import jdk.vm.ci.code.Architecture;
53 import jdk.vm.ci.common.InitTimer;
54 import jdk.vm.ci.services.Services;
55 
56 /**
57  * A factory that creates the {@link CompilerConfiguration} the compiler will use. Each factory must
58  * have a unique {@link #name} and {@link #autoSelectionPriority}. The latter imposes a total
59  * ordering between factories for the purpose of auto-selecting the factory to use.
60  */
61 public abstract class CompilerConfigurationFactory implements Comparable<CompilerConfigurationFactory> {
62 
63     enum ShowConfigurationLevel {
64         none,
65         info,
66         verbose
67     }
68 
69     static class Options {
70         // @formatter:off
71         @Option(help = "Names the compiler configuration to use. If omitted, the compiler configuration " +
72                        "with the highest auto-selection priority is used. To see the set of available configurations, " +
73                        "supply the value 'help' to this option.", type = OptionType.Expert, stability = OptionStability.STABLE)
74         public static final OptionKey<String> CompilerConfiguration = new OptionKey<>(null);
75         @Option(help = "Writes to the VM log information about the compiler configuration selected.", type = OptionType.User, stability = OptionStability.STABLE)
76         public static final OptionKey<ShowConfigurationLevel> ShowConfiguration = new EnumOptionKey<>(ShowConfigurationLevel.none);
77         // @formatter:on
78     }
79 
80     /**
81      * The name of this factory. This must be unique across all factory instances and is used when
82      * selecting a factory based on the value of {@link Options#CompilerConfiguration}.
83      */
84     private final String name;
85 
86     /**
87      * The priority of this factory. This must be unique across all factory instances and is used
88      * when selecting a factory when {@link Options#CompilerConfiguration} is omitted
89      */
90     private final int autoSelectionPriority;
91 
CompilerConfigurationFactory(String name, int autoSelectionPriority)92     protected CompilerConfigurationFactory(String name, int autoSelectionPriority) {
93         this.name = name;
94         this.autoSelectionPriority = autoSelectionPriority;
95     }
96 
createCompilerConfiguration()97     public abstract CompilerConfiguration createCompilerConfiguration();
98 
99     /**
100      * Collect the set of available {@linkplain HotSpotBackendFactory backends} for this compiler
101      * configuration.
102      */
createBackendMap()103     public BackendMap createBackendMap() {
104         // default to backend with the same name as the compiler configuration
105         return new DefaultBackendMap(name);
106     }
107 
108     /**
109      * Returns a name that should uniquely identify this compiler configuration.
110      */
getName()111     public final String getName() {
112         return name;
113     }
114 
115     public interface BackendMap {
getBackendFactory(Architecture arch)116         HotSpotBackendFactory getBackendFactory(Architecture arch);
117     }
118 
119     public static class DefaultBackendMap implements BackendMap {
120 
121         private final EconomicMap<Class<? extends Architecture>, HotSpotBackendFactory> backends = EconomicMap.create();
122 
123         @SuppressWarnings("try")
DefaultBackendMap(String backendName)124         public DefaultBackendMap(String backendName) {
125             try (InitTimer t = timer("HotSpotBackendFactory.register")) {
126                 for (HotSpotBackendFactory backend : GraalServices.load(HotSpotBackendFactory.class)) {
127                     if (backend.getName().equals(backendName)) {
128                         Class<? extends Architecture> arch = backend.getArchitecture();
129                         HotSpotBackendFactory oldEntry = backends.put(arch, backend);
130                         assert oldEntry == null || oldEntry == backend : "duplicate Graal backend";
131                     }
132                 }
133             }
134         }
135 
136         @Override
getBackendFactory(Architecture arch)137         public final HotSpotBackendFactory getBackendFactory(Architecture arch) {
138             return backends.get(arch.getClass());
139         }
140     }
141 
142     @Override
compareTo(CompilerConfigurationFactory o)143     public int compareTo(CompilerConfigurationFactory o) {
144         if (autoSelectionPriority > o.autoSelectionPriority) {
145             return -1;
146         }
147         if (autoSelectionPriority < o.autoSelectionPriority) {
148             return 1;
149         }
150         assert this == o : "distinct compiler configurations cannot have the same auto selection priority";
151         return 0;
152     }
153 
154     /**
155      * Asserts uniqueness of {@link #name} and {@link #autoSelectionPriority} for {@code factory} in
156      * {@code factories}.
157      */
checkUnique(CompilerConfigurationFactory factory, List<CompilerConfigurationFactory> factories)158     private static boolean checkUnique(CompilerConfigurationFactory factory, List<CompilerConfigurationFactory> factories) {
159         for (CompilerConfigurationFactory other : factories) {
160             if (other != factory) {
161                 assert !other.name.equals(factory.name) : factory.getClass().getName() + " cannot have the same selector as " + other.getClass().getName() + ": " + factory.name;
162                 assert other.autoSelectionPriority != factory.autoSelectionPriority : factory.getClass().getName() + " cannot have the same auto-selection priority as " +
163                                 other.getClass().getName() +
164                                 ": " + factory.autoSelectionPriority;
165             }
166         }
167         return true;
168     }
169 
170     /**
171      * @return sorted list of {@link CompilerConfigurationFactory}s
172      */
173     @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "false positive on dead store to `candidates`")
getAllCandidates()174     private static List<CompilerConfigurationFactory> getAllCandidates() {
175         List<CompilerConfigurationFactory> candidates = new ArrayList<>();
176         for (CompilerConfigurationFactory candidate : GraalServices.load(CompilerConfigurationFactory.class)) {
177             assert checkUnique(candidate, candidates);
178             candidates.add(candidate);
179         }
180         Collections.sort(candidates);
181         return candidates;
182     }
183 
184     /**
185      * Selects and instantiates a {@link CompilerConfigurationFactory}. The selection algorithm is
186      * as follows: if {@code name} is non-null, then select the factory with the same name else if
187      * {@code Options.CompilerConfiguration.getValue()} is non-null then select the factory whose
188      * name matches the value else select the factory with the highest
189      * {@link #autoSelectionPriority} value.
190      *
191      * @param name the name of the compiler configuration to select (optional)
192      */
193     @SuppressWarnings("try")
selectFactory(String name, OptionValues options)194     public static CompilerConfigurationFactory selectFactory(String name, OptionValues options) {
195         CompilerConfigurationFactory factory = null;
196         try (InitTimer t = timer("CompilerConfigurationFactory.selectFactory")) {
197             String value = name == null ? Options.CompilerConfiguration.getValue(options) : name;
198             if ("help".equals(value)) {
199                 System.out.println("The available compiler configurations are:");
200                 for (CompilerConfigurationFactory candidate : getAllCandidates()) {
201                     System.out.println("    " + candidate.name);
202                 }
203                 HotSpotGraalServices.exit(0);
204             } else if (value != null) {
205                 for (CompilerConfigurationFactory candidate : GraalServices.load(CompilerConfigurationFactory.class)) {
206                     if (candidate.name.equals(value)) {
207                         factory = candidate;
208                         break;
209                     }
210                 }
211                 if (factory == null) {
212                     throw new GraalError("Compiler configuration '%s' not found. Available configurations are: %s", value,
213                                     getAllCandidates().stream().map(c -> c.name).collect(Collectors.joining(", ")));
214                 }
215             } else {
216                 List<CompilerConfigurationFactory> candidates = getAllCandidates();
217                 if (candidates.isEmpty()) {
218                     throw new GraalError("No %s providers found", CompilerConfigurationFactory.class.getName());
219                 }
220                 factory = candidates.get(0);
221             }
222         }
223         assert factory != null;
224 
225         ShowConfigurationLevel level = Options.ShowConfiguration.getValue(options);
226         if (level != ShowConfigurationLevel.none) {
227             switch (level) {
228                 case info: {
229                     printConfigInfo(factory);
230                     break;
231                 }
232                 case verbose: {
233                     printConfigInfo(factory);
234                     CompilerConfiguration config = factory.createCompilerConfiguration();
235                     TTY.println("High tier: " + phaseNames(config.createHighTier(options)));
236                     TTY.println("Mid tier: " + phaseNames(config.createMidTier(options)));
237                     TTY.println("Low tier: " + phaseNames(config.createLowTier(options)));
238                     TTY.println("Pre regalloc stage: " + phaseNames(config.createPreAllocationOptimizationStage(options)));
239                     TTY.println("Regalloc stage: " + phaseNames(config.createAllocationStage(options)));
240                     TTY.println("Post regalloc stage: " + phaseNames(config.createPostAllocationOptimizationStage(options)));
241                     config.createAllocationStage(options);
242                     break;
243                 }
244             }
245         }
246         return factory;
247     }
248 
printConfigInfo(CompilerConfigurationFactory factory)249     private static void printConfigInfo(CompilerConfigurationFactory factory) {
250         Object location = Services.IS_IN_NATIVE_IMAGE ? "JVMCI native library" : factory.getClass().getResource(factory.getClass().getSimpleName() + ".class");
251         TTY.printf("Using compiler configuration '%s' provided by %s loaded from %s%n", factory.name, factory.getClass().getName(), location);
252     }
253 
phaseNames(PhaseSuite<C> suite)254     private static <C> List<String> phaseNames(PhaseSuite<C> suite) {
255         Collection<BasePhase<? super C>> phases = suite.getPhases();
256         List<String> res = new ArrayList<>(phases.size());
257         for (BasePhase<?> phase : phases) {
258             res.add(phase.contractorName());
259         }
260         Collections.sort(res);
261         return res;
262     }
263 
phaseNames(LIRPhaseSuite<C> suite)264     private static <C> List<String> phaseNames(LIRPhaseSuite<C> suite) {
265         List<LIRPhase<C>> phases = suite.getPhases();
266         List<String> res = new ArrayList<>(phases.size());
267         for (LIRPhase<?> phase : phases) {
268             res.add(phase.getClass().getName());
269         }
270         Collections.sort(res);
271         return res;
272     }
273 }
274