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.lang.reflect.Constructor;
016    
017    import org.jikesrvm.VM;
018    import org.jikesrvm.compilers.opt.OptOptions;
019    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
020    import org.jikesrvm.compilers.opt.ir.IR;
021    
022    /**
023     * Compiler phases all extend this abstract class.
024     * All compiler phases must provide implementations of
025     * two abstract methods:
026     * <ul>
027     *  <li> getName:  return a String that is the name of the phase
028     *  <li> perform:  actually do the work of the phase
029     * </ul>
030     *
031     * <p> By default, a new instance of the phase is created each time
032     * shouldPerform is called.  This instance is discarded as soon
033     * as shouldPerform completes. Therefore, it is allowable
034     * (and is suggested when necessary) for subclasses
035     * to use their instance fields to hold per-compilation state.
036     * To be more concrete, the pattern of use is:
037     * <pre>
038     *  newExecution(ir).performPhase(ir).
039     * </pre>
040     * @see OptimizationPlanAtomicElement#perform
041     *
042     * <p> NOTE: compiler phases that do not need to use instance
043     * fields to hold per-compilation state may override
044     * <code> newExecution() </code> to return this.  Doing so may lead to
045     * memory leaks and concurrent access problems, so this should be done
046     * with great care!
047     */
048    public abstract class CompilerPhase {
049    
050      /**
051       * The plan element that contains this phase.
052       * Only useful if the phase wants to gather additional statistics
053       * for a measure compilation report.
054       */
055      protected OptimizationPlanAtomicElement container;
056    
057      /**
058       * Arguments to constructor that copies this phase
059       */
060      private final Object[] initargs;
061    
062      /**
063       * Constructor
064       */
065      public CompilerPhase() {
066        initargs = null;
067      }
068    
069      /**
070       * Constructor
071       *
072       * @param initargs arguments used when constructing copies of this phase
073       */
074      public CompilerPhase(Object[] initargs) {
075        this.initargs = initargs;
076      }
077    
078      /**
079       * @return a String which is the name of the phase.
080       */
081      public abstract String getName();
082    
083      /**
084       * This is the method that actually does the work of the phase.
085       *
086       * @param ir the IR on which to apply the phase
087       */
088      public abstract void perform(IR ir);
089    
090      /**
091       * This method determines if the phase should be run, based on the
092       * Options object it is passed.
093       * By default, phases are always performed.
094       * Subclasses should override this method if they only want
095       * to be performed conditionally.
096       *
097       * @param options the compiler options for the compilation
098       * @return true if the phase should be performed
099       */
100      public boolean shouldPerform(OptOptions options) {
101        return true;
102      }
103    
104      /**
105       * Returns true if the phase wants the IR dumped before and/or after it runs.
106       * By default, printing is not enabled.
107       * Subclasses should override this method if they want to provide IR dumping.
108       *
109       * @param options the compiler options for the compilation
110       * @param before true when invoked before perform, false otherwise.
111       * @return true if the IR should be printed, false otherwise.
112       */
113      public boolean printingEnabled(OptOptions options, boolean before) {
114        return false;
115      }
116    
117      /**
118       * Called when printing a measure compilation report to enable a phase
119       * to report additional phase-specific statistics.
120       */
121      public void reportAdditionalStats() {}
122    
123      /**
124       * This method is called immediately before performPhase. Phases
125       * that do not need to create a new instance for each execution may
126       * override this method to return this, but this must be done
127       * carefully! Classes that don't override this method need to
128       * override getClassConstructor.
129       *
130       * @param ir the IR that is about to be passed to performPhase
131       * @return an opt compiler phase on which performPhase may be invoked.
132       */
133      public CompilerPhase newExecution(IR ir) {
134        Constructor<CompilerPhase> cons = getClassConstructor();
135        if (cons != null) {
136          try {
137            return cons.newInstance(initargs);
138          } catch (Exception e) {
139            throw new Error("Failed to create phase " + this.getClass() + " with constructor " + cons, e);
140          }
141        } else {
142          throw new Error("Error, no constructor found in phase " +
143                          this.getClass() +
144                          " make sure a public constructor is declared");
145        }
146      }
147    
148      /**
149       * Get a constructor object for this compiler phase
150       *
151       * @return exception/null as this phase can't be created
152       */
153      public Constructor<CompilerPhase> getClassConstructor() {
154        OptimizingCompilerException.UNREACHABLE();
155        return null;
156      }
157    
158      /**
159       * Given the name of a compiler phase return the default (no
160       * argument) constructor for it.
161       */
162      protected static Constructor<CompilerPhase> getCompilerPhaseConstructor(Class<? extends CompilerPhase> klass) {
163        return getCompilerPhaseConstructor(klass, null);
164      }
165    
166      /**
167       * Given the name of a compiler phase return the default (no
168       * argument) constructor for it.
169       */
170      protected static Constructor<CompilerPhase> getCompilerPhaseConstructor(Class<? extends CompilerPhase> phaseType,
171                                                                                  Class<?>[] initTypes) {
172        try {
173          @SuppressWarnings("unchecked") // We are explicitly breaking type safety
174              Constructor<CompilerPhase> constructor =
175              (Constructor<CompilerPhase>) phaseType.getConstructor(initTypes);
176          return constructor;
177        } catch (NoSuchMethodException e) {
178          throw new Error("Constructor not found in " + phaseType.getName() + " compiler phase", e);
179        }
180      }
181    
182      /**
183       * Set the containing optimization plan element for this phase
184       */
185      public final void setContainer(OptimizationPlanAtomicElement atomEl) {
186        container = atomEl;
187      }
188    
189      /**
190       * Runs a phase by calling perform on the supplied IR surrounded by
191       * printing/messaging/debugging glue.
192       * @param ir the IR object on which to do the work of the phase.
193       */
194      public final void performPhase(IR ir) {
195        if (printingEnabled(ir.options, true)) {
196          if (!ir.options.hasMETHOD_TO_PRINT() || ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString())) {
197            // only print above certain opt level.
198            //if (ir.options.getOptLevel() >= ir.options.IR_PRINT_LEVEL) {
199            dumpIR(ir, "Before " + getName());
200            //}
201          }
202        }
203        if (ir.options.PRINT_PHASES) VM.sysWrite(getName() + " (" + ir.method.toString()+ ")");
204    
205        perform(ir);                // DOIT!!
206    
207        if (ir.options.PRINT_PHASES) VM.sysWrite(" done\n");
208        if (ir.options.PRINT_ALL_IR || printingEnabled(ir.options, false)) {
209          if (!ir.options.hasMETHOD_TO_PRINT() || ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString())) {
210            // only print when above certain opt level
211            if (ir.options.getOptLevel() >= ir.options.PRINT_IR_LEVEL) {
212              dumpIR(ir, "After " + getName());
213            }
214          }
215        }
216    
217        if (IR.PARANOID) verify(ir);
218      }
219    
220      /**
221       * Prints the IR, optionally including the CFG
222       *
223       * @param ir the IR to print
224       * @param tag a String to use in the start/end message of the IR dump
225       */
226      public static void dumpIR(IR ir, String tag) {
227        dumpIR(ir, tag, false);
228      }
229    
230      /**
231       * Prints the IR, optionally including the CFG
232       *
233       * @param ir the IR to print
234       * @param forceCFG should the CFG be printed, independent of the value of ir.options.PRINT_CFG?
235       * @param tag a String to use in the start/end message of the IR dump
236       */
237      public static void dumpIR(IR ir, String tag, boolean forceCFG) {
238        System.out.println("********* START OF IR DUMP  " + tag + "   FOR " + ir.method);
239        ir.printInstructions();
240        if (forceCFG || ir.options.PRINT_CFG) {
241          ir.cfg.printDepthFirst();
242        }
243        System.out.println("*********   END OF IR DUMP  " + tag + "   FOR " + ir.method);
244      }
245    
246      /**
247       * Verify the IR.
248       * Written as a non-final virtual method to allow late stages in the
249       * compilation pipeline (eg ConvertMIR2MC) to skip verification.
250       *
251       * @param ir the IR to verify
252       */
253      public void verify(IR ir) {
254        ir.verify(getName(), true);
255      }
256    }