001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.compilers.opt.driver;
014
015import static org.jikesrvm.runtime.ExitStatus.EXIT_STATUS_OPT_COMPILER_FAILED;
016
017import java.util.Vector;
018
019import org.jikesrvm.VM;
020import org.jikesrvm.adaptive.recompilation.CompilerDNA;
021import org.jikesrvm.classloader.NormalMethod;
022import org.jikesrvm.classloader.RVMClass;
023import org.jikesrvm.classloader.RVMMethod;
024import org.jikesrvm.classloader.TypeReference;
025import org.jikesrvm.compilers.baseline.BaselineCompiler;
026import org.jikesrvm.compilers.baseline.EdgeCounts;
027import org.jikesrvm.compilers.common.BootImageCompiler;
028import org.jikesrvm.compilers.common.CompiledMethod;
029import org.jikesrvm.compilers.opt.MagicNotImplementedException;
030import org.jikesrvm.compilers.opt.OptOptions;
031import org.jikesrvm.compilers.opt.OptimizingCompilerException;
032import org.jikesrvm.runtime.Callbacks;
033
034/**
035 * Use optimizing compiler to build virtual machine boot image.
036 */
037public final class OptimizingBootImageCompiler extends BootImageCompiler {
038
039  // Cache objects needed to cons up compilation plans
040  private final Vector<OptimizationPlanElement[]> optimizationPlans = new Vector<OptimizationPlanElement[]>();
041  private final Vector<Boolean> optimizationPlanLocks = new Vector<Boolean>();
042  private final Vector<OptOptions> options = new Vector<OptOptions>();
043  private final OptOptions masterOptions = new OptOptions();
044
045  /**
046   * If excludePattern is {@code null}, all methods are opt-compiled (or attempted).
047   * Otherwise, methods that match the pattern are not opt-compiled.
048   * In any case, the class OptSaveVolatile is always opt-compiled.
049   */
050  private String excludePattern;
051
052  private boolean match(RVMMethod method) {
053    if (excludePattern == null) return true;
054    RVMClass cls = method.getDeclaringClass();
055    String clsName = cls.toString();
056    if (clsName.compareTo("org.jikesrvm.compilers.opt.runtimesupport.OptSaveVolatile") == 0) return true;
057    String methodName = method.getName().toString();
058    String fullName = clsName + "." + methodName;
059    return (fullName.indexOf(excludePattern)) < 0;
060  }
061
062  @Override
063  protected void initCompiler(String[] args) {
064    try {
065      BaselineCompiler.initOptions();
066      VM.sysWrite("BootImageCompiler: init (opt compiler)\n");
067
068      // Writing a boot image is a little bit special.  We're not really
069      // concerned about compile time, but we do care a lot about the quality
070      // and stability of the generated code.  Set the options accordingly.
071      OptimizingCompiler.setBootOptions(masterOptions);
072
073      // Allow further customization by the user.
074      for (int i = 0, n = args.length; i < n; i++) {
075        String arg = args[i];
076        if (!masterOptions.processAsOption("-X:bc:", arg)) {
077          if (arg.startsWith("exclude=")) {
078            excludePattern = arg.substring(8);
079          } else {
080            VM.sysWrite("BootImageCompiler: Unrecognized argument " + arg + "; ignoring\n");
081          }
082        }
083      }
084      EdgeCounts.loadCountsFromFileIfAvailable(masterOptions.PROFILE_EDGE_COUNT_INPUT_FILE);
085      OptimizingCompiler.init(masterOptions);
086    } catch (OptimizingCompilerException e) {
087      String msg = "BootImageCompiler: Compiler failed during initialization: " + e + "\n";
088      if (e.isFatal) {
089        // An unexpected error when building the opt boot image should be fatal
090        e.printStackTrace();
091        System.exit(EXIT_STATUS_OPT_COMPILER_FAILED);
092      } else {
093        VM.sysWrite(msg);
094      }
095    }
096  }
097
098  @Override
099  protected CompiledMethod compileMethod(NormalMethod method, TypeReference[] params) {
100    if (method.hasNoOptCompileAnnotation()) {
101      return baselineCompile(method);
102    } else {
103      CompiledMethod cm = null;
104      OptimizingCompilerException escape = new OptimizingCompilerException(false);
105      try {
106        Callbacks.notifyMethodCompile(method, CompiledMethod.OPT);
107        boolean include = match(method);
108        if (!include) {
109          throw escape;
110        }
111        int freeOptimizationPlan = getFreeOptimizationPlan();
112        OptimizationPlanElement[] optimizationPlan = optimizationPlans.get(freeOptimizationPlan);
113        CompilationPlan cp =
114          new CompilationPlan(method, params, optimizationPlan, null, options.get(freeOptimizationPlan));
115        cm = OptimizingCompiler.compile(cp);
116        if (VM.BuildForAdaptiveSystem) {
117          /* We can't accurately measure compilation time on Host JVM, so just approximate with DNA */
118          int compilerId = CompilerDNA.getCompilerConstant(cp.options.getOptLevel());
119          cm.setCompilationTime((float)CompilerDNA.estimateCompileTime(compilerId, method));
120        }
121        releaseOptimizationPlan(freeOptimizationPlan);
122        return cm;
123      } catch (OptimizingCompilerException e) {
124        if (e.isFatal) {
125          // An unexpected error when building the opt boot image should be fatal
126          VM.sysWriteln("Error compiling method: " + method);
127          e.printStackTrace();
128          System.exit(EXIT_STATUS_OPT_COMPILER_FAILED);
129        } else {
130          boolean printMsg = true;
131          boolean expected = false;
132          if (e instanceof MagicNotImplementedException) {
133            printMsg = !((MagicNotImplementedException) e).isExpected;
134            expected = ((MagicNotImplementedException) e).isExpected;
135          }
136          if (e == escape) {
137            printMsg = false;
138          }
139          if (printMsg) {
140            if (e.toString().indexOf("method excluded") >= 0) {
141              String msg = "BootImageCompiler: " + method + " excluded from opt-compilation\n";
142              VM.sysWrite(msg);
143            } else {
144              String msg = "BootImageCompiler: can't optimize \"" + method + "\" (error was: " + e + ")\n";
145              VM.sysWrite(msg);
146            }
147          } else if (!expected && e != escape) {
148            // Treat any unexpected OptimizingCompilerException that occur
149            // when compiling the boot image as fatal.
150            throw new Error(e);
151          }
152        }
153        return baselineCompile(method);
154      }
155    }
156  }
157
158  private CompiledMethod baselineCompile(NormalMethod method) {
159    Callbacks.notifyMethodCompile(method, CompiledMethod.BASELINE);
160    CompiledMethod cm = BaselineCompiler.compile(method);
161    /* We can't accurately measure compilation time on Host JVM, so just approximate with DNA */
162    cm.setCompilationTime((float)CompilerDNA.estimateCompileTime(CompilerDNA.BASELINE, method));
163    return cm;
164  }
165
166  /**
167   * Return an optimization plan that isn't in use
168   * @return optimization plan
169   */
170  private int getFreeOptimizationPlan() {
171    // Find plan
172    synchronized (optimizationPlanLocks) {
173      for (int i = 0; i < optimizationPlanLocks.size(); i++) {
174        if (!optimizationPlanLocks.get(i)) {
175          optimizationPlanLocks.set(i, Boolean.TRUE);
176          return i;
177        }
178      }
179      // Find failed, so create new plan
180      OptimizationPlanElement[] optimizationPlan;
181      OptOptions cloneOptions = masterOptions.dup();
182      optimizationPlan = OptimizationPlanner.createOptimizationPlan(cloneOptions);
183      optimizationPlans.add(optimizationPlan);
184      optimizationPlanLocks.add(Boolean.TRUE);
185      options.add(cloneOptions);
186      return optimizationPlanLocks.size() - 1;
187    }
188  }
189
190  /**
191   * Release an optimization plan
192   * @param plan an optimization plan
193   */
194  private void releaseOptimizationPlan(int plan) {
195    synchronized (optimizationPlanLocks) {
196      optimizationPlanLocks.set(plan, Boolean.FALSE);
197    }
198  }
199}