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.escape;
014
015import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER;
016import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT;
017import static org.jikesrvm.compilers.opt.ir.Operators.NEWARRAY_opcode;
018import static org.jikesrvm.compilers.opt.ir.Operators.NEW_opcode;
019
020import org.jikesrvm.classloader.RVMType;
021import org.jikesrvm.compilers.opt.DefUse;
022import org.jikesrvm.compilers.opt.LocalConstantProp;
023import org.jikesrvm.compilers.opt.LocalCopyProp;
024import org.jikesrvm.compilers.opt.OptimizingCompilerException;
025import org.jikesrvm.compilers.opt.OptOptions;
026import org.jikesrvm.compilers.opt.Simple;
027import org.jikesrvm.compilers.opt.driver.CompilerPhase;
028import org.jikesrvm.compilers.opt.driver.OptimizationPlanCompositeElement;
029import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
030import org.jikesrvm.compilers.opt.ir.Call;
031import org.jikesrvm.compilers.opt.ir.IR;
032import org.jikesrvm.compilers.opt.ir.Instruction;
033import org.jikesrvm.compilers.opt.ir.New;
034import org.jikesrvm.compilers.opt.ir.NewArray;
035import org.jikesrvm.compilers.opt.ir.Register;
036import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
037import org.jikesrvm.compilers.opt.ir.operand.Operand;
038import 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 */
047public 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 &amp; uses of "synchronized"
152   * objects to defs &amp; 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(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   *
173   * PRECONDITION: register lists computed and valid
174   * @param r the object's register
175   * @return whether synchronization occurs on the given object
176   */
177  private static boolean synchronizesOn(Register r) {
178    // walk through uses of r
179    for (RegisterOperand use = r.useList; use != null; use = use.getNext()) {
180      Instruction s = use.instruction;
181      if (s.operator() == MONITORENTER) {
182        return true;
183      }
184      if (s.operator() == MONITOREXIT) {
185        return true;
186      }
187      // check if this instruction invokes a synchronized method on the
188      // object
189      // we must pass the following conditions:
190      //        1. the method is not static
191      //        2. it is actually invoked on the register operand in question
192      //        3. the method is synchronized
193      if (Call.conforms(s)) {
194        MethodOperand mo = Call.getMethod(s);
195        if (!mo.isStatic()) {
196          Operand invokee = Call.getParam(s, 0);
197          if (invokee == use) {
198            if (!mo.hasPreciseTarget()) return true; // if I don't know exactly what is called, assume the worse
199            if (mo.getTarget().isSynchronized()) {
200              return true;
201            }
202          }
203        }
204      }
205    }
206    return false;
207  }
208
209  /**
210   * Generate an object which will perform scalar replacement of
211   * an aggregate allocated by a given instruction
212   *
213   * <p> PRECONDITION: objects returned by this allocation site do NOT escape
214   *                 the current method
215   *
216   * @param inst the allocation site
217   * @param ir controlling ir
218   * @return an AggregateReplacer specialized to the allocation site,
219   *            null if no legal transformation found
220   */
221  private AggregateReplacer getAggregateReplacer(Instruction inst, IR ir) {
222    OptOptions options = ir.options;
223    RVMType t = null;
224    if (inst.getOpcode() == NEW_opcode) {
225      t = New.getType(inst).getVMType();
226    } else if (inst.getOpcode() == NEWARRAY_opcode) {
227      t = NewArray.getType(inst).getVMType();
228    } else {
229      throw new OptimizingCompilerException("Logic Error in EscapeTransformations");
230    }
231
232    // first attempt to perform scalar replacement for an object
233    if (t.isClassType() && options.ESCAPE_SCALAR_REPLACE_AGGREGATES) {
234      return ObjectReplacer.getReplacer(inst, ir);
235    }
236    // attempt to perform scalar replacement on a short array
237    if (t.isArrayType() && options.ESCAPE_SCALAR_REPLACE_AGGREGATES) {
238      return ShortArrayReplacer.getReplacer(inst, ir);
239    }
240    return null;
241  }
242}