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 @Override 110 public void transform() { 111 // store the object's fields in a ArrayList 112 ArrayList<RVMField> fields = getFieldsAsArrayList(klass); 113 // create a scalar for each field. initialize the scalar to 114 // default values before the object's def 115 RegisterOperand[] scalars = new RegisterOperand[fields.size()]; 116 RegisterOperand def = reg.defList; 117 Instruction defI = def.instruction; 118 for (int i = 0; i < fields.size(); i++) { 119 RVMField f = fields.get(i); 120 Operand defaultValue = IRTools.getDefaultOperand(f.getType()); 121 scalars[i] = IRTools.moveIntoRegister(ir.regpool, defI, defaultValue); 122 scalars[i].setType(f.getType()); 123 } 124 transform2(this.reg, defI, scalars, fields, null); 125 } 126 127 private void transform2(Register reg, Instruction defI, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) { 128 final boolean DEBUG = false; 129 130 // now remove the def 131 if (DEBUG) { 132 System.out.println("Removing " + defI); 133 } 134 DefUse.removeInstructionAndUpdateDU(defI); 135 // now handle the uses 136 for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) { 137 scalarReplace(use, scalars, fields, visited); 138 } 139 } 140 141 /** 142 * Returns a ArrayList<RVMField>, holding the fields of the object 143 * @param klass the type of the object 144 */ 145 private static ArrayList<RVMField> getFieldsAsArrayList(RVMClass klass) { 146 ArrayList<RVMField> v = new ArrayList<RVMField>(); 147 for (RVMField field : klass.getInstanceFields()) { 148 v.add(field); 149 } 150 return v; 151 } 152 153 /** 154 * @param r the register holding the object reference 155 * @param _klass the type of the object to replace 156 * @param i the IR 157 */ 158 private ObjectReplacer(Register r, RVMClass _klass, IR i) { 159 reg = r; 160 klass = _klass; 161 ir = i; 162 } 163 164 /** 165 * Replace a given use of a object with its scalar equivalent 166 * 167 * @param use the use to replace 168 * @param scalars an array of scalar register operands to replace 169 * the object's fields with 170 */ 171 private void scalarReplace(RegisterOperand use, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) { 172 Instruction inst = use.instruction; 173 try{ 174 switch (inst.getOpcode()) { 175 case PUTFIELD_opcode: { 176 FieldReference fr = PutField.getLocation(inst).getFieldRef(); 177 if (VM.VerifyAssertions) VM._assert(fr.isResolved()); 178 RVMField f = fr.peekResolvedField(); 179 int index = fields.indexOf(f); 180 TypeReference type = scalars[index].getType(); 181 Operator moveOp = IRTools.getMoveOp(type); 182 Instruction i = Move.create(moveOp, scalars[index].copyRO(), PutField.getClearValue(inst)); 183 inst.insertBefore(i); 184 DefUse.removeInstructionAndUpdateDU(inst); 185 DefUse.updateDUForNewInstruction(i); 186 } 187 break; 188 case GETFIELD_opcode: { 189 FieldReference fr = GetField.getLocation(inst).getFieldRef(); 190 if (VM.VerifyAssertions) VM._assert(fr.isResolved()); 191 RVMField f = fr.peekResolvedField(); 192 int index = fields.indexOf(f); 193 TypeReference type = scalars[index].getType(); 194 Operator moveOp = IRTools.getMoveOp(type); 195 Instruction i = Move.create(moveOp, GetField.getClearResult(inst), scalars[index].copyRO()); 196 inst.insertBefore(i); 197 DefUse.removeInstructionAndUpdateDU(inst); 198 DefUse.updateDUForNewInstruction(i); 199 } 200 break; 201 case MONITORENTER_opcode: 202 inst.insertBefore(Empty.create(READ_CEILING)); 203 DefUse.removeInstructionAndUpdateDU(inst); 204 break; 205 case MONITOREXIT_opcode: 206 inst.insertBefore(Empty.create(WRITE_FLOOR)); 207 DefUse.removeInstructionAndUpdateDU(inst); 208 break; 209 case CALL_opcode: 210 case NULL_CHECK_opcode: 211 // (SJF) TODO: Why wasn't this caught by BC2IR for 212 // java.lang.Double.<init> (Ljava/lang/String;)V ? 213 DefUse.removeInstructionAndUpdateDU(inst); 214 break; 215 case CHECKCAST_opcode: 216 case CHECKCAST_NOTNULL_opcode: 217 case CHECKCAST_UNRESOLVED_opcode: { 218 // We cannot handle removing the checkcast if the result of the 219 // checkcast test is unknown 220 TypeReference lhsType = TypeCheck.getType(inst).getTypeRef(); 221 if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.YES) { 222 if (visited == null) { 223 visited = new HashSet<Register>(); 224 } 225 Register copy = TypeCheck.getResult(inst).getRegister(); 226 if(!visited.contains(copy)) { 227 visited.add(copy); 228 transform2(copy, inst, scalars, fields, visited); 229 // NB will remove inst 230 } else { 231 DefUse.removeInstructionAndUpdateDU(inst); 232 } 233 } else { 234 Instruction i2 = Trap.create(TRAP, null, TrapCodeOperand.CheckCast()); 235 DefUse.replaceInstructionAndUpdateDU(inst, i2); 236 } 237 } 238 break; 239 case INSTANCEOF_opcode: 240 case INSTANCEOF_NOTNULL_opcode: 241 case INSTANCEOF_UNRESOLVED_opcode: { 242 // We cannot handle removing the instanceof if the result of the 243 // instanceof test is unknown 244 TypeReference lhsType = InstanceOf.getType(inst).getTypeRef(); 245 Instruction i2; 246 if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.YES) { 247 i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(1)); 248 } else { 249 i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(0)); 250 } 251 DefUse.replaceInstructionAndUpdateDU(inst, i2); 252 } 253 break; 254 case GET_OBJ_TIB_opcode: { 255 Instruction i2 = Move.create(REF_MOVE, GuardedUnary.getClearResult(inst), new TIBConstantOperand(klass)); 256 DefUse.replaceInstructionAndUpdateDU(inst, i2); 257 } 258 break; 259 case REF_MOVE_opcode: { 260 if (visited == null) { 261 visited = new HashSet<Register>(); 262 } 263 Register copy = Move.getResult(use.instruction).getRegister(); 264 if(!visited.contains(copy)) { 265 visited.add(copy); 266 transform2(copy, inst, scalars, fields, visited); 267 // NB will remove inst 268 } else { 269 DefUse.removeInstructionAndUpdateDU(inst); 270 } 271 } 272 break; 273 default: 274 throw new OptimizingCompilerException("ObjectReplacer: unexpected use " + inst); 275 } 276 } catch (Exception e) { 277 OptimizingCompilerException oe = new OptimizingCompilerException("Error handling use ("+ use +") of: "+ inst); 278 oe.initCause(e); 279 throw oe; 280 } 281 } 282 283 /** 284 * Some cases we don't handle yet. TODO: handle them. 285 */ 286 private static boolean containsUnsupportedUse(IR ir, Register reg, RVMClass klass, Set<Register> visited) { 287 for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) { 288 switch (use.instruction.getOpcode()) { 289 case MUST_IMPLEMENT_INTERFACE_opcode: 290 case REF_IFCMP_opcode: 291 return true; 292 case CHECKCAST_opcode: 293 case CHECKCAST_NOTNULL_opcode: 294 case CHECKCAST_UNRESOLVED_opcode: { 295 // We cannot handle removing the checkcast if the result of the 296 // checkcast test is unknown 297 TypeReference lhsType = TypeCheck.getType(use.instruction).getTypeRef(); 298 byte ans = ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()); 299 if (ans == OptConstants.MAYBE) { 300 return true; 301 } else if (ans == OptConstants.YES) { 302 // handle as a move 303 if (visited == null) { 304 visited = new HashSet<Register>(); 305 } 306 Register copy = TypeCheck.getResult(use.instruction).getRegister(); 307 if(!visited.contains(copy)) { 308 visited.add(copy); 309 if(containsUnsupportedUse(ir, copy, klass, visited)) { 310 return true; 311 } 312 } 313 } 314 } 315 break; 316 case INSTANCEOF_opcode: 317 case INSTANCEOF_NOTNULL_opcode: 318 case INSTANCEOF_UNRESOLVED_opcode: { 319 // We cannot handle removing the instanceof if the result of the 320 // instanceof test is unknown 321 TypeReference lhsType = InstanceOf.getType(use.instruction).getTypeRef(); 322 if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.MAYBE) { 323 return true; 324 } 325 } 326 break; 327 case REF_MOVE_opcode: 328 if (visited == null) { 329 visited = new HashSet<Register>(); 330 } 331 Register copy = Move.getResult(use.instruction).getRegister(); 332 if(!visited.contains(copy)) { 333 visited.add(copy); 334 if(containsUnsupportedUse(ir, copy, klass, visited)) { 335 return true; 336 } 337 } 338 break; 339 case BOOLEAN_CMP_INT_opcode: 340 case BOOLEAN_CMP_ADDR_opcode: 341 case LONG_STORE_opcode: 342 throw new OptimizingCompilerException("Unexpected use of reference considered for replacement: " + use.instruction + " in " + ir.method); 343 } 344 } 345 return false; 346 } 347 }