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 java.util.ArrayList;
016 import java.util.HashSet;
017 import java.util.Set;
018
019 import org.jikesrvm.VM;
020 import org.jikesrvm.classloader.RVMClass;
021 import org.jikesrvm.classloader.RVMField;
022 import org.jikesrvm.classloader.FieldReference;
023 import org.jikesrvm.classloader.TypeReference;
024 import org.jikesrvm.compilers.opt.ClassLoaderProxy;
025 import org.jikesrvm.compilers.opt.DefUse;
026 import org.jikesrvm.compilers.opt.OptimizingCompilerException;
027 import org.jikesrvm.compilers.opt.driver.OptConstants;
028 import org.jikesrvm.compilers.opt.ir.Empty;
029 import org.jikesrvm.compilers.opt.ir.GetField;
030 import org.jikesrvm.compilers.opt.ir.GuardedUnary;
031 import org.jikesrvm.compilers.opt.ir.InstanceOf;
032 import org.jikesrvm.compilers.opt.ir.Move;
033 import org.jikesrvm.compilers.opt.ir.New;
034 import org.jikesrvm.compilers.opt.ir.IR;
035 import org.jikesrvm.compilers.opt.ir.IRTools;
036 import org.jikesrvm.compilers.opt.ir.Instruction;
037 import org.jikesrvm.compilers.opt.ir.Operator;
038 import org.jikesrvm.compilers.opt.ir.Trap;
039 import org.jikesrvm.compilers.opt.ir.TypeCheck;
040
041 import static org.jikesrvm.compilers.opt.ir.IRTools.IC;
042 import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR_opcode;
043 import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_INT_opcode;
044 import static org.jikesrvm.compilers.opt.ir.Operators.CALL_opcode;
045 import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_NOTNULL_opcode;
046 import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_UNRESOLVED_opcode;
047 import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_opcode;
048 import static org.jikesrvm.compilers.opt.ir.Operators.GETFIELD_opcode;
049 import static org.jikesrvm.compilers.opt.ir.Operators.GET_OBJ_TIB_opcode;
050 import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_NOTNULL_opcode;
051 import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_UNRESOLVED_opcode;
052 import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_opcode;
053 import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE;
054 import static org.jikesrvm.compilers.opt.ir.Operators.LONG_STORE_opcode;
055 import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER_opcode;
056 import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT_opcode;
057 import static org.jikesrvm.compilers.opt.ir.Operators.MUST_IMPLEMENT_INTERFACE_opcode;
058 import static org.jikesrvm.compilers.opt.ir.Operators.NULL_CHECK_opcode;
059 import static org.jikesrvm.compilers.opt.ir.Operators.PUTFIELD_opcode;
060 import static org.jikesrvm.compilers.opt.ir.Operators.READ_CEILING;
061 import static org.jikesrvm.compilers.opt.ir.Operators.REF_IFCMP_opcode;
062 import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE;
063 import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE_opcode;
064 import static org.jikesrvm.compilers.opt.ir.Operators.TRAP;
065 import static org.jikesrvm.compilers.opt.ir.Operators.WRITE_FLOOR;
066 import org.jikesrvm.compilers.opt.ir.Register;
067 import org.jikesrvm.compilers.opt.ir.PutField;
068 import org.jikesrvm.compilers.opt.ir.operand.Operand;
069 import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
070 import org.jikesrvm.compilers.opt.ir.operand.TIBConstantOperand;
071 import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand;
072
073 /**
074 * Class that performs scalar replacement of aggregates for non-array
075 * objects
076 */
077 final class ObjectReplacer implements AggregateReplacer {
078 /**
079 * type of the object
080 */
081 private final RVMClass klass;
082 /**
083 * the IR
084 */
085 private final IR ir;
086 /**
087 * the register holding the object reference
088 */
089 private final Register reg;
090
091 /**
092 * Return an object representing this transformation for a given
093 * allocation site
094 *
095 * @param inst the allocation site
096 * @param ir
097 * @return the object, or null if illegal
098 */
099 public static ObjectReplacer getReplacer(Instruction inst, IR ir) {
100 Register r = New.getResult(inst).getRegister();
101 RVMClass klass = New.getType(inst).getVMType().asClass();
102 // TODO :handle these cases
103 if (klass.hasFinalizer() || containsUnsupportedUse(ir, r, klass, null)) {
104 return null;
105 }
106 return new ObjectReplacer(r, klass, ir);
107 }
108
109 /**
110 * Perform the transformation
111 */
112 public void transform() {
113 // store the object's fields in a ArrayList
114 ArrayList<RVMField> fields = getFieldsAsArrayList(klass);
115 // create a scalar for each field. initialize the scalar to
116 // default values before the object's def
117 RegisterOperand[] scalars = new RegisterOperand[fields.size()];
118 RegisterOperand def = reg.defList;
119 Instruction defI = def.instruction;
120 for (int i = 0; i < fields.size(); i++) {
121 RVMField f = fields.get(i);
122 Operand defaultValue = IRTools.getDefaultOperand(f.getType());
123 scalars[i] = IRTools.moveIntoRegister(ir.regpool, defI, defaultValue);
124 scalars[i].setType(f.getType());
125 }
126 transform2(this.reg, defI, scalars, fields, null);
127 }
128
129 private void transform2(Register reg, Instruction defI, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) {
130 final boolean DEBUG = false;
131
132 // now remove the def
133 if (DEBUG) {
134 System.out.println("Removing " + defI);
135 }
136 DefUse.removeInstructionAndUpdateDU(defI);
137 // now handle the uses
138 for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
139 scalarReplace(use, scalars, fields, visited);
140 }
141 }
142
143 /**
144 * Returns a ArrayList<RVMField>, holding the fields of the object
145 * @param klass the type of the object
146 */
147 private static ArrayList<RVMField> getFieldsAsArrayList(RVMClass klass) {
148 ArrayList<RVMField> v = new ArrayList<RVMField>();
149 for (RVMField field : klass.getInstanceFields()) {
150 v.add(field);
151 }
152 return v;
153 }
154
155 /**
156 * @param r the register holding the object reference
157 * @param _klass the type of the object to replace
158 * @param i the IR
159 */
160 private ObjectReplacer(Register r, RVMClass _klass, IR i) {
161 reg = r;
162 klass = _klass;
163 ir = i;
164 }
165
166 /**
167 * Replace a given use of a object with its scalar equivalent
168 *
169 * @param use the use to replace
170 * @param scalars an array of scalar register operands to replace
171 * the object's fields with
172 */
173 private void scalarReplace(RegisterOperand use, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) {
174 Instruction inst = use.instruction;
175 try{
176 switch (inst.getOpcode()) {
177 case PUTFIELD_opcode: {
178 FieldReference fr = PutField.getLocation(inst).getFieldRef();
179 if (VM.VerifyAssertions) VM._assert(fr.isResolved());
180 RVMField f = fr.peekResolvedField();
181 int index = fields.indexOf(f);
182 TypeReference type = scalars[index].getType();
183 Operator moveOp = IRTools.getMoveOp(type);
184 Instruction i = Move.create(moveOp, scalars[index].copyRO(), PutField.getClearValue(inst));
185 inst.insertBefore(i);
186 DefUse.removeInstructionAndUpdateDU(inst);
187 DefUse.updateDUForNewInstruction(i);
188 }
189 break;
190 case GETFIELD_opcode: {
191 FieldReference fr = GetField.getLocation(inst).getFieldRef();
192 if (VM.VerifyAssertions) VM._assert(fr.isResolved());
193 RVMField f = fr.peekResolvedField();
194 int index = fields.indexOf(f);
195 TypeReference type = scalars[index].getType();
196 Operator moveOp = IRTools.getMoveOp(type);
197 Instruction i = Move.create(moveOp, GetField.getClearResult(inst), scalars[index].copyRO());
198 inst.insertBefore(i);
199 DefUse.removeInstructionAndUpdateDU(inst);
200 DefUse.updateDUForNewInstruction(i);
201 }
202 break;
203 case MONITORENTER_opcode:
204 inst.insertBefore(Empty.create(READ_CEILING));
205 DefUse.removeInstructionAndUpdateDU(inst);
206 break;
207 case MONITOREXIT_opcode:
208 inst.insertBefore(Empty.create(WRITE_FLOOR));
209 DefUse.removeInstructionAndUpdateDU(inst);
210 break;
211 case CALL_opcode:
212 case NULL_CHECK_opcode:
213 // (SJF) TODO: Why wasn't this caught by BC2IR for
214 // java.lang.Double.<init> (Ljava/lang/String;)V ?
215 DefUse.removeInstructionAndUpdateDU(inst);
216 break;
217 case CHECKCAST_opcode:
218 case CHECKCAST_NOTNULL_opcode:
219 case CHECKCAST_UNRESOLVED_opcode: {
220 // We cannot handle removing the checkcast if the result of the
221 // checkcast test is unknown
222 TypeReference lhsType = TypeCheck.getType(inst).getTypeRef();
223 if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.YES) {
224 if (visited == null) {
225 visited = new HashSet<Register>();
226 }
227 Register copy = TypeCheck.getResult(inst).getRegister();
228 if(!visited.contains(copy)) {
229 visited.add(copy);
230 transform2(copy, inst, scalars, fields, visited);
231 // NB will remove inst
232 } else {
233 DefUse.removeInstructionAndUpdateDU(inst);
234 }
235 } else {
236 Instruction i2 = Trap.create(TRAP, null, TrapCodeOperand.CheckCast());
237 DefUse.replaceInstructionAndUpdateDU(inst, i2);
238 }
239 }
240 break;
241 case INSTANCEOF_opcode:
242 case INSTANCEOF_NOTNULL_opcode:
243 case INSTANCEOF_UNRESOLVED_opcode: {
244 // We cannot handle removing the instanceof if the result of the
245 // instanceof test is unknown
246 TypeReference lhsType = InstanceOf.getType(inst).getTypeRef();
247 Instruction i2;
248 if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.YES) {
249 i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(1));
250 } else {
251 i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(0));
252 }
253 DefUse.replaceInstructionAndUpdateDU(inst, i2);
254 }
255 break;
256 case GET_OBJ_TIB_opcode: {
257 Instruction i2 = Move.create(REF_MOVE, GuardedUnary.getClearResult(inst), new TIBConstantOperand(klass));
258 DefUse.replaceInstructionAndUpdateDU(inst, i2);
259 }
260 break;
261 case REF_MOVE_opcode: {
262 if (visited == null) {
263 visited = new HashSet<Register>();
264 }
265 Register copy = Move.getResult(use.instruction).getRegister();
266 if(!visited.contains(copy)) {
267 visited.add(copy);
268 transform2(copy, inst, scalars, fields, visited);
269 // NB will remove inst
270 } else {
271 DefUse.removeInstructionAndUpdateDU(inst);
272 }
273 }
274 break;
275 default:
276 throw new OptimizingCompilerException("ObjectReplacer: unexpected use " + inst);
277 }
278 } catch (Exception e) {
279 OptimizingCompilerException oe = new OptimizingCompilerException("Error handling use ("+ use +") of: "+ inst);
280 oe.initCause(e);
281 throw oe;
282 }
283 }
284
285 /**
286 * Some cases we don't handle yet. TODO: handle them.
287 */
288 private static boolean containsUnsupportedUse(IR ir, Register reg, RVMClass klass, Set<Register> visited) {
289 for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
290 switch (use.instruction.getOpcode()) {
291 case MUST_IMPLEMENT_INTERFACE_opcode:
292 case REF_IFCMP_opcode:
293 return true;
294 case CHECKCAST_opcode:
295 case CHECKCAST_NOTNULL_opcode:
296 case CHECKCAST_UNRESOLVED_opcode: {
297 // We cannot handle removing the checkcast if the result of the
298 // checkcast test is unknown
299 TypeReference lhsType = TypeCheck.getType(use.instruction).getTypeRef();
300 byte ans = ClassLoaderProxy.includesType(lhsType, klass.getTypeRef());
301 if (ans == OptConstants.MAYBE) {
302 return true;
303 } else if (ans == OptConstants.YES) {
304 // handle as a move
305 if (visited == null) {
306 visited = new HashSet<Register>();
307 }
308 Register copy = TypeCheck.getResult(use.instruction).getRegister();
309 if(!visited.contains(copy)) {
310 visited.add(copy);
311 if(containsUnsupportedUse(ir, copy, klass, visited)) {
312 return true;
313 }
314 }
315 }
316 }
317 break;
318 case INSTANCEOF_opcode:
319 case INSTANCEOF_NOTNULL_opcode:
320 case INSTANCEOF_UNRESOLVED_opcode: {
321 // We cannot handle removing the instanceof if the result of the
322 // instanceof test is unknown
323 TypeReference lhsType = InstanceOf.getType(use.instruction).getTypeRef();
324 if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.MAYBE) {
325 return true;
326 }
327 }
328 break;
329 case REF_MOVE_opcode:
330 if (visited == null) {
331 visited = new HashSet<Register>();
332 }
333 Register copy = Move.getResult(use.instruction).getRegister();
334 if(!visited.contains(copy)) {
335 visited.add(copy);
336 if(containsUnsupportedUse(ir, copy, klass, visited)) {
337 return true;
338 }
339 }
340 break;
341 case BOOLEAN_CMP_INT_opcode:
342 case BOOLEAN_CMP_ADDR_opcode:
343 case LONG_STORE_opcode:
344 throw new OptimizingCompilerException("Unexpected use of reference considered for replacement: " + use.instruction + " in " + ir.method);
345 }
346 }
347 return false;
348 }
349 }