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.OptionStability;
  46 import org.graalvm.compiler.options.OptionType;
  47 import org.graalvm.compiler.options.OptionValues;
  48 import org.graalvm.compiler.phases.BasePhase;
  49 import org.graalvm.compiler.phases.PhaseSuite;
  50 import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
  51 import org.graalvm.compiler.serviceprovider.GraalServices;
  52 
  53 import jdk.vm.ci.code.Architecture;
  54 import jdk.vm.ci.common.InitTimer;
  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 
  92     protected CompilerConfigurationFactory(String name, int autoSelectionPriority) {
  93         this.name = name;
  94         this.autoSelectionPriority = autoSelectionPriority;
  95     }
  96 
  97     public abstract CompilerConfiguration createCompilerConfiguration();
  98 
  99     /**
 100      * Collect the set of available {@linkplain HotSpotBackendFactory backends} for this compiler
 101      * configuration.
 102      */
 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      */
 111     public final String getName() {
 112         return name;
 113     }
 114 
 115     public interface BackendMap {
 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")
 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
 137         public final HotSpotBackendFactory getBackendFactory(Architecture arch) {
 138             return backends.get(arch.getClass());
 139         }
 140     }
 141 
 142     @Override
 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      */
 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`")
 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")
 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 
 249     private static void printConfigInfo(CompilerConfigurationFactory factory) {
 250         URL location = 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 
 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 
 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 }