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 }