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     */
013    package org.jikesrvm.compilers.opt.driver;
014    
015    import java.util.Vector;
016    
017    import org.jikesrvm.VM;
018    import org.jikesrvm.Callbacks;
019    import org.jikesrvm.adaptive.recompilation.CompilerDNA;
020    import org.jikesrvm.classloader.RVMClass;
021    import org.jikesrvm.classloader.RVMMethod;
022    import org.jikesrvm.classloader.NormalMethod;
023    import org.jikesrvm.classloader.TypeReference;
024    import org.jikesrvm.compilers.baseline.BaselineCompiler;
025    import org.jikesrvm.compilers.baseline.EdgeCounts;
026    import org.jikesrvm.compilers.common.BootImageCompiler;
027    import org.jikesrvm.compilers.common.CompiledMethod;
028    import org.jikesrvm.compilers.opt.MagicNotImplementedException;
029    import org.jikesrvm.compilers.opt.OptOptions;
030    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
031    
032    /**
033     * Use optimizing compiler to build virtual machine boot image.
034     */
035    public final class OptimizingBootImageCompiler extends BootImageCompiler {
036    
037      // Cache objects needed to cons up compilation plans
038      private final Vector<OptimizationPlanElement[]> optimizationPlans = new Vector<OptimizationPlanElement[]>();
039      private final Vector<Boolean> optimizationPlanLocks = new Vector<Boolean>();
040      private final Vector<OptOptions> options = new Vector<OptOptions>();
041      private final OptOptions masterOptions = new OptOptions();
042    
043      // If excludePattern is null, all methods are opt-compiled (or attempted).
044      // Otherwise, methods that match the pattern are not opt-compiled.
045      // In any case, the class OptSaveVolatile is always opt-compiled.
046      //
047      private String excludePattern;
048    
049      private boolean match(RVMMethod method) {
050        if (excludePattern == null) return true;
051        RVMClass cls = method.getDeclaringClass();
052        String clsName = cls.toString();
053        if (clsName.compareTo("org.jikesrvm.compilers.opt.runtimesupport.OptSaveVolatile") == 0) return true;
054        String methodName = method.getName().toString();
055        String fullName = clsName + "." + methodName;
056        return (fullName.indexOf(excludePattern)) < 0;
057      }
058    
059      /**
060       * Initialize boot image compiler.
061       * @param args command line arguments to the bootimage compiler
062       */
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.boot(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(VM.EXIT_STATUS_OPT_COMPILER_FAILED);
092          } else {
093            VM.sysWrite(msg);
094          }
095        }
096      }
097    
098      /**
099       * Compile a method with bytecodes.
100       * @param method the method to compile
101       * @return the compiled method
102       */
103      protected CompiledMethod compileMethod(NormalMethod method, TypeReference[] params) {
104        if (method.hasNoOptCompileAnnotation()) {
105          return baselineCompile(method);
106        } else {
107          CompiledMethod cm = null;
108          OptimizingCompilerException escape = new OptimizingCompilerException(false);
109          try {
110            Callbacks.notifyMethodCompile(method, CompiledMethod.OPT);
111            boolean include = match(method);
112            if (!include) {
113              throw escape;
114            }
115            int freeOptimizationPlan = getFreeOptimizationPlan();
116            OptimizationPlanElement[] optimizationPlan = optimizationPlans.get(freeOptimizationPlan);
117            CompilationPlan cp =
118              new CompilationPlan(method, params, optimizationPlan, null, options.get(freeOptimizationPlan));
119            cm = OptimizingCompiler.compile(cp);
120            if (VM.BuildForAdaptiveSystem) {
121              /* We can't accurately measure compilation time on Host JVM, so just approximate with DNA */
122              int compilerId = CompilerDNA.getCompilerConstant(cp.options.getOptLevel());
123              cm.setCompilationTime((float)CompilerDNA.estimateCompileTime(compilerId, method));
124            }
125            releaseOptimizationPlan(freeOptimizationPlan);
126            return cm;
127          } catch (OptimizingCompilerException e) {
128            if (e.isFatal) {
129              // An unexpected error when building the opt boot image should be fatal
130              VM.sysWriteln("Error compiling method: "+method);
131              e.printStackTrace();
132              System.exit(VM.EXIT_STATUS_OPT_COMPILER_FAILED);
133            } else {
134              boolean printMsg = true;
135              if (e instanceof MagicNotImplementedException) {
136                printMsg = !((MagicNotImplementedException) e).isExpected;
137              }
138              if (e == escape) {
139                printMsg = false;
140              }
141              if (printMsg) {
142                if (e.toString().indexOf("method excluded") >= 0) {
143                  String msg = "BootImageCompiler: " + method + " excluded from opt-compilation\n";
144                  VM.sysWrite(msg);
145                } else {
146                  String msg = "BootImageCompiler: can't optimize \"" + method + "\" (error was: " + e + ")\n";
147                  VM.sysWrite(msg);
148                }
149              }
150            }
151            return baselineCompile(method);
152          }
153        }
154      }
155    
156      private CompiledMethod baselineCompile(NormalMethod method) {
157        Callbacks.notifyMethodCompile(method, CompiledMethod.BASELINE);
158        CompiledMethod cm = BaselineCompiler.compile(method);
159        /* We can't accurately measure compilation time on Host JVM, so just approximate with DNA */
160        cm.setCompilationTime((float)CompilerDNA.estimateCompileTime(CompilerDNA.BASELINE, method));
161        return cm;
162      }
163    
164      /**
165       * Return an optimization plan that isn't in use
166       * @return optimization plan
167       */
168      private int getFreeOptimizationPlan() {
169        // Find plan
170        synchronized (optimizationPlanLocks) {
171          for (int i = 0; i < optimizationPlanLocks.size(); i++) {
172            if (!optimizationPlanLocks.get(i)) {
173              optimizationPlanLocks.set(i, Boolean.TRUE);
174              return i;
175            }
176          }
177          // Find failed, so create new plan
178          OptimizationPlanElement[] optimizationPlan;
179          OptOptions cloneOptions = masterOptions.dup();
180          optimizationPlan = OptimizationPlanner.createOptimizationPlan(cloneOptions);
181          optimizationPlans.addElement(optimizationPlan);
182          optimizationPlanLocks.addElement(Boolean.TRUE);
183          options.addElement(cloneOptions);
184          return optimizationPlanLocks.size() - 1;
185        }
186      }
187    
188      /**
189       * Release an optimization plan
190       * @param plan an optimization plan
191       */
192      private void releaseOptimizationPlan(int plan) {
193        synchronized (optimizationPlanLocks) {
194          optimizationPlanLocks.set(plan, Boolean.FALSE);
195        }
196      }
197    }