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