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.escape;
014    
015    import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER;
016    import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT;
017    import static org.jikesrvm.compilers.opt.ir.Operators.NEWARRAY_opcode;
018    import static org.jikesrvm.compilers.opt.ir.Operators.NEW_opcode;
019    
020    import org.jikesrvm.classloader.RVMType;
021    import org.jikesrvm.compilers.opt.DefUse;
022    import org.jikesrvm.compilers.opt.LocalConstantProp;
023    import org.jikesrvm.compilers.opt.LocalCopyProp;
024    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
025    import org.jikesrvm.compilers.opt.OptOptions;
026    import org.jikesrvm.compilers.opt.Simple;
027    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
028    import org.jikesrvm.compilers.opt.driver.OptimizationPlanCompositeElement;
029    import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
030    import org.jikesrvm.compilers.opt.ir.Call;
031    import org.jikesrvm.compilers.opt.ir.IR;
032    import org.jikesrvm.compilers.opt.ir.Instruction;
033    import org.jikesrvm.compilers.opt.ir.New;
034    import org.jikesrvm.compilers.opt.ir.NewArray;
035    import org.jikesrvm.compilers.opt.ir.Register;
036    import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
037    import org.jikesrvm.compilers.opt.ir.operand.Operand;
038    import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
039    
040    /**
041     * Transformations that use escape analysis.
042     * <ul>
043     *  <li> 1. synchronization removal
044     *  <li> 2. scalar replacement of aggregates and short arrays
045     * </ul>
046     */
047    public class EscapeTransformations extends CompilerPhase {
048    
049      /**
050       * Transforms to clean the IR prior to another round of escape transformations
051       */
052      private static final OptimizationPlanElement escapeCleanUp =
053        OptimizationPlanCompositeElement.compose("Clean up escape transformations",
054                                                 new Object[]{new LocalCopyProp(),
055                                                              new LocalConstantProp(),
056                                                              new Simple(0, true, false, false, false)});
057    
058      /**
059       * Return this instance of this phase. This phase contains no
060       * per-compilation instance fields.
061       * @param ir not used
062       * @return this
063       */
064      public CompilerPhase newExecution(IR ir) {
065        return this;
066      }
067    
068      public final boolean shouldPerform(OptOptions options) {
069        return options.ESCAPE_MONITOR_REMOVAL || options.ESCAPE_SCALAR_REPLACE_AGGREGATES;
070      }
071    
072      public final String getName() {
073        return "Escape Transformations";
074      }
075    
076      public final boolean printingEnabled(OptOptions options, boolean before) {
077        return false;
078      }
079    
080      /**
081       * Perform the transformations
082       *
083       * @param ir IR for the target method
084       */
085      public void perform(IR ir) {
086        // perform simple optimizations to increase efficacy
087        DefUse.computeDU(ir);
088        DefUse.recomputeSSA(ir);
089        SimpleEscape analyzer = new SimpleEscape();
090        // do multiple passes to catch chains of objects that can be removed
091        boolean removedAggregate;
092        do {
093          removedAggregate = false;
094          FI_EscapeSummary summary = analyzer.simpleEscapeAnalysis(ir);
095          // pass through registers. look for registers that point
096          // to objects that do not escape. When found,
097          // perform the transformations
098          for (Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = reg.getNext()) {
099            // check if register is SSA
100            if (!reg.isSSA()) {
101              continue;
102            }
103            // The following can occur for guards. Why?
104            if (reg.defList == null) {
105              continue;
106            }
107            // *********************************************************
108            // check if def is allocation. If so, try scalar replacement
109            // of aggregates
110            // *********************************************************
111            Instruction def = reg.defList.instruction;
112            if (ir.options.ESCAPE_SCALAR_REPLACE_AGGREGATES && summary.isMethodLocal(reg)) {
113              AggregateReplacer s = null;
114              if ((def.getOpcode() == NEW_opcode) || (def.getOpcode() == NEWARRAY_opcode)) {
115                s = getAggregateReplacer(def, ir);
116              }
117              if (s != null) {
118                // org.jikesrvm.VM.sysWrite("Scalar replacing "+def+" in "+ir.method+"\n");
119                s.transform();
120                removedAggregate = true;
121              }
122            }
123            // *********************************************************
124            // Now remove synchronizations
125            // *********************************************************
126            if (ir.options.ESCAPE_MONITOR_REMOVAL && summary.isThreadLocal(reg)) {
127              UnsyncReplacer unsync = null;
128              if ((def.getOpcode() == NEW_opcode) || (def.getOpcode() == NEWARRAY_opcode)) {
129                unsync = getUnsyncReplacer(reg, def, ir);
130              }
131              if (unsync != null) {
132                // VM.sysWrite("Removing synchronization on "+def+" in "+ir.method+"\n");
133                unsync.transform();
134              }
135            }
136          }
137          if (removedAggregate) {
138            // do quick clean up of IR
139            // org.jikesrvm.VM.sysWrite("Cleaning up IR in "+ir.method+"\n");
140            escapeCleanUp.perform(ir);
141          }
142        } while (removedAggregate);
143      }
144    
145      /**
146       * Generate an object which transforms defs & uses of "synchronized"
147       * objects to defs & uses of "unsynchronized" objects
148       *
149       * <p> PRECONDITION: objects pointed to by reg do NOT escape
150       *
151       * @param reg the pointer whose defs and uses should be transformed
152       * @param inst the allocation site
153       * @param ir controlling ir
154       * @return an UnsyncReplacer specialized to the allocation site,
155       *            null if no legal transformation found
156       */
157      private UnsyncReplacer getUnsyncReplacer(Register reg, Instruction inst, IR ir) {
158        if (!synchronizesOn(ir, reg)) {
159          return null;
160        }
161        return UnsyncReplacer.getReplacer(inst, ir);
162      }
163    
164      /**
165       * Is there an instruction in this IR which causes synchronization
166       * on an object pointed to by a particular register?
167       * PRECONDITION: register lists computed and valid
168       */
169      private static boolean synchronizesOn(IR ir, Register r) {
170        // walk through uses of r
171        for (RegisterOperand use = r.useList; use != null; use = use.getNext()) {
172          Instruction s = use.instruction;
173          if (s.operator == MONITORENTER) {
174            return true;
175          }
176          if (s.operator == MONITOREXIT) {
177            return true;
178          }
179          // check if this instruction invokes a synchronized method on the
180          // object
181          // we must pass the following conditions:
182          //        1. the method is not static
183          //        2. it is actually invoked on the register operand in question
184          //        3. the method is synchronized
185          if (Call.conforms(s)) {
186            MethodOperand mo = Call.getMethod(s);
187            if (!mo.isStatic()) {
188              Operand invokee = Call.getParam(s, 0);
189              if (invokee == use) {
190                if (!mo.hasPreciseTarget()) return true; // if I don't know exactly what is called, assume the worse
191                if (mo.getTarget().isSynchronized()) {
192                  return true;
193                }
194              }
195            }
196          }
197        }
198        return false;
199      }
200    
201      /**
202       * Generate an object which will perform scalar replacement of
203       * an aggregate allocated by a given instruction
204       *
205       * <p> PRECONDITION: objects returned by this allocation site do NOT escape
206       *                 the current method
207       *
208       * @param inst the allocation site
209       * @param ir controlling ir
210       * @return an AggregateReplacer specialized to the allocation site,
211       *            null if no legal transformation found
212       */
213      private AggregateReplacer getAggregateReplacer(Instruction inst, IR ir) {
214        OptOptions options = ir.options;
215        RVMType t = null;
216        if (inst.getOpcode() == NEW_opcode) {
217          t = New.getType(inst).getVMType();
218        } else if (inst.getOpcode() == NEWARRAY_opcode) {
219          t = NewArray.getType(inst).getVMType();
220        } else {
221          throw new OptimizingCompilerException("Logic Error in EscapeTransformations");
222        }
223    
224        // first attempt to perform scalar replacement for an object
225        if (t.isClassType() && options.ESCAPE_SCALAR_REPLACE_AGGREGATES) {
226          return ObjectReplacer.getReplacer(inst, ir);
227        }
228        // attempt to perform scalar replacement on a short array
229        if (t.isArrayType() && options.ESCAPE_SCALAR_REPLACE_AGGREGATES) {
230          return ShortArrayReplacer.getReplacer(inst, ir);
231        }
232        return null;
233      }
234    }