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