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