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.HashSet; 016 import java.util.Set; 017 import org.jikesrvm.VM; 018 import org.jikesrvm.classloader.RVMArray; 019 import org.jikesrvm.classloader.RVMType; 020 import org.jikesrvm.classloader.TypeReference; 021 import org.jikesrvm.compilers.opt.ClassLoaderProxy; 022 import org.jikesrvm.compilers.opt.DefUse; 023 import org.jikesrvm.compilers.opt.OptimizingCompilerException; 024 import org.jikesrvm.compilers.opt.ir.ALoad; 025 import org.jikesrvm.compilers.opt.ir.AStore; 026 import org.jikesrvm.compilers.opt.ir.BoundsCheck; 027 import org.jikesrvm.compilers.opt.ir.CondMove; 028 import org.jikesrvm.compilers.opt.ir.GuardedUnary; 029 import org.jikesrvm.compilers.opt.ir.InstanceOf; 030 import org.jikesrvm.compilers.opt.ir.Move; 031 import org.jikesrvm.compilers.opt.ir.NewArray; 032 import org.jikesrvm.compilers.opt.ir.NullCheck; 033 import org.jikesrvm.compilers.opt.ir.IR; 034 import org.jikesrvm.compilers.opt.ir.IRTools; 035 import org.jikesrvm.compilers.opt.ir.Instruction; 036 import org.jikesrvm.compilers.opt.ir.Operator; 037 import org.jikesrvm.compilers.opt.ir.Trap; 038 import org.jikesrvm.compilers.opt.ir.TrapIf; 039 import org.jikesrvm.compilers.opt.ir.TypeCheck; 040 import org.jikesrvm.compilers.opt.driver.OptConstants; 041 import static org.jikesrvm.compilers.opt.ir.IRTools.IC; 042 import static org.jikesrvm.compilers.opt.ir.Operators.BOUNDS_CHECK_opcode; 043 import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_ALOAD_opcode; 044 import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_ASTORE_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.DOUBLE_ALOAD_opcode; 049 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_ASTORE_opcode; 050 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ALOAD_opcode; 051 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ASTORE_opcode; 052 import static org.jikesrvm.compilers.opt.ir.Operators.GET_OBJ_TIB_opcode; 053 import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE; 054 import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_NOTNULL_opcode; 055 import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_UNRESOLVED_opcode; 056 import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_opcode; 057 import static org.jikesrvm.compilers.opt.ir.Operators.INT_ALOAD_opcode; 058 import static org.jikesrvm.compilers.opt.ir.Operators.INT_ASTORE_opcode; 059 import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE; 060 import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ALOAD_opcode; 061 import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ASTORE_opcode; 062 import static org.jikesrvm.compilers.opt.ir.Operators.NEWARRAY; 063 import static org.jikesrvm.compilers.opt.ir.Operators.NEWOBJMULTIARRAY_opcode; 064 import static org.jikesrvm.compilers.opt.ir.Operators.NULL_CHECK_opcode; 065 import static org.jikesrvm.compilers.opt.ir.Operators.OBJARRAY_STORE_CHECK_NOTNULL_opcode; 066 import static org.jikesrvm.compilers.opt.ir.Operators.OBJARRAY_STORE_CHECK_opcode; 067 import static org.jikesrvm.compilers.opt.ir.Operators.REF_ALOAD_opcode; 068 import static org.jikesrvm.compilers.opt.ir.Operators.REF_ASTORE_opcode; 069 import static org.jikesrvm.compilers.opt.ir.Operators.REF_IFCMP_opcode; 070 import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE; 071 import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE_opcode; 072 import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_ALOAD_opcode; 073 import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_ASTORE_opcode; 074 import static org.jikesrvm.compilers.opt.ir.Operators.TRAP; 075 import static org.jikesrvm.compilers.opt.ir.Operators.TRAP_IF; 076 import static org.jikesrvm.compilers.opt.ir.Operators.UBYTE_ALOAD_opcode; 077 import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_ALOAD_opcode; 078 import org.jikesrvm.compilers.opt.ir.Register; 079 import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand; 080 import org.jikesrvm.compilers.opt.ir.operand.Operand; 081 import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; 082 import org.jikesrvm.compilers.opt.ir.operand.TIBConstantOperand; 083 import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand; 084 import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand; 085 086 /** 087 * Class that performs scalar replacement of short arrays 088 */ 089 final class ShortArrayReplacer implements AggregateReplacer { 090 /** 091 * number of elements in the array 092 */ 093 private final int size; 094 /** 095 * type of the array 096 */ 097 private final RVMArray vmArray; 098 /** 099 * the register holding the array reference 100 */ 101 private final Register reg; 102 /** 103 * the governing IR 104 */ 105 private final IR ir; 106 107 /** 108 * @param r the register holding the array reference 109 * @param a the type of the array to replace 110 * @param s the size of the array to replace 111 * @param i the IR 112 */ 113 private ShortArrayReplacer(Register r, RVMArray a, int s, IR i) { 114 reg = r; 115 vmArray = a; 116 size = s; 117 ir = i; 118 } 119 120 /** 121 * Return an object representing this transformation for a given 122 * allocation site 123 * 124 * @param inst the allocation site 125 * @param ir 126 * @return the object, or null if illegal 127 */ 128 public static ShortArrayReplacer getReplacer(Instruction inst, IR ir) { 129 if (inst.operator != NEWARRAY) { 130 return null; 131 } 132 Operand size = NewArray.getSize(inst); 133 if (!size.isIntConstant()) { 134 return null; 135 } 136 int s = size.asIntConstant().value; 137 if (s > ir.options.ESCAPE_MAX_ARRAY_SIZE) { 138 return null; 139 } 140 if (s < 0) { 141 return null; 142 } 143 Register r = NewArray.getResult(inst).getRegister(); 144 RVMArray a = NewArray.getType(inst).getVMType().asArray(); 145 // TODO :handle these cases 146 if (containsUnsupportedUse(ir, r, s, a, null)) { 147 return null; 148 } 149 return new ShortArrayReplacer(r, a, s, ir); 150 } 151 152 @Override 153 public void transform() { 154 // first set up temporary scalars for the array elements 155 // initialize them before the def. 156 RegisterOperand[] scalars = new RegisterOperand[size]; 157 TypeReference elementType = vmArray.getElementType().getTypeRef(); 158 RegisterOperand def = reg.defList; 159 Instruction defI = def.instruction; 160 Operand defaultValue = IRTools.getDefaultOperand(elementType); 161 for (int i = 0; i < size; i++) { 162 scalars[i] = IRTools.moveIntoRegister(elementType, IRTools.getMoveOp(elementType), ir.regpool, defI, defaultValue.copy()); 163 } 164 transform2(this.reg, defI, scalars); 165 } 166 private void transform2(Register reg, Instruction defI, RegisterOperand[] scalars) { 167 final boolean DEBUG = false; 168 169 // now remove the def 170 if (DEBUG) { 171 System.out.println("Removing " + defI); 172 } 173 DefUse.removeInstructionAndUpdateDU(defI); 174 // now handle the uses 175 for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) { 176 scalarReplace(use, scalars, null); 177 } 178 } 179 180 /** 181 * Replace a given use of an array with its scalar equivalent. 182 * 183 * @param use the use to replace 184 * @param scalars an array of scalar register operands to replace 185 * the array with 186 */ 187 private void scalarReplace(RegisterOperand use, RegisterOperand[] scalars, Set<Register> visited) { 188 Instruction inst = use.instruction; 189 RVMType type = vmArray.getElementType(); 190 switch (inst.getOpcode()) { 191 case INT_ALOAD_opcode: 192 case LONG_ALOAD_opcode: 193 case FLOAT_ALOAD_opcode: 194 case DOUBLE_ALOAD_opcode: 195 case BYTE_ALOAD_opcode: 196 case UBYTE_ALOAD_opcode: 197 case USHORT_ALOAD_opcode: 198 case SHORT_ALOAD_opcode: 199 case REF_ALOAD_opcode: { 200 // Create use of scalar or eliminate unreachable instruction because 201 // of a trap 202 if (ALoad.getIndex(inst).isIntConstant()) { 203 Operator moveOp = IRTools.getMoveOp(type.getTypeRef()); 204 int index = ALoad.getIndex(inst).asIntConstant().value; 205 if (index >= 0 && index < size) { 206 Instruction i2 = Move.create(moveOp, ALoad.getClearResult(inst), scalars[index].copyRO()); 207 DefUse.replaceInstructionAndUpdateDU(inst, i2); 208 } else { 209 DefUse.removeInstructionAndUpdateDU(inst); 210 } 211 } else { 212 if (VM.BuildForIA32) { 213 if (size == 0) { 214 DefUse.removeInstructionAndUpdateDU(inst); 215 } else if (size == 1) { 216 int index = 0; 217 Operator moveOp = IRTools.getMoveOp(type.getTypeRef()); 218 Instruction i2 = Move.create(moveOp, ALoad.getClearResult(inst), scalars[index].copyRO()); 219 DefUse.replaceInstructionAndUpdateDU(inst, i2); 220 } else { 221 Operator moveOp = IRTools.getCondMoveOp(type.getTypeRef()); 222 Instruction i2 = CondMove.create(moveOp, ALoad.getClearResult(inst), 223 ALoad.getIndex(inst), IC(0), ConditionOperand.EQUAL(), 224 scalars[0].copyRO(), scalars[1].copyRO()); 225 DefUse.replaceInstructionAndUpdateDU(inst, i2); 226 } 227 } else { 228 if (size == 1) { 229 int index = 0; 230 Operator moveOp = IRTools.getMoveOp(type.getTypeRef()); 231 Instruction i2 = Move.create(moveOp, ALoad.getClearResult(inst), scalars[index].copyRO()); 232 DefUse.replaceInstructionAndUpdateDU(inst, i2); 233 } else { 234 DefUse.removeInstructionAndUpdateDU(inst); 235 } 236 } 237 } 238 } 239 break; 240 case INT_ASTORE_opcode: 241 case LONG_ASTORE_opcode: 242 case FLOAT_ASTORE_opcode: 243 case DOUBLE_ASTORE_opcode: 244 case BYTE_ASTORE_opcode: 245 case SHORT_ASTORE_opcode: 246 case REF_ASTORE_opcode: { 247 // Create move to scalar or eliminate unreachable instruction because 248 // of a trap 249 if (AStore.getIndex(inst).isIntConstant()) { 250 int index = AStore.getIndex(inst).asIntConstant().value; 251 if (index >= 0 && index < size) { 252 Operator moveOp = IRTools.getMoveOp(type.getTypeRef()); 253 Instruction i2 = Move.create(moveOp, scalars[index].copyRO(), AStore.getClearValue(inst)); 254 DefUse.replaceInstructionAndUpdateDU(inst, i2); 255 } else { 256 DefUse.removeInstructionAndUpdateDU(inst); 257 } 258 } else { 259 if (VM.BuildForIA32) { 260 if (size == 0) { 261 DefUse.removeInstructionAndUpdateDU(inst); 262 } else if (size == 1) { 263 int index = 0; 264 Operator moveOp = IRTools.getMoveOp(type.getTypeRef()); 265 Instruction i2 = Move.create(moveOp, scalars[index].copyRO(), AStore.getClearValue(inst)); 266 DefUse.replaceInstructionAndUpdateDU(inst, i2); 267 } else { 268 Operator moveOp = IRTools.getCondMoveOp(type.getTypeRef()); 269 Operand value = AStore.getClearValue(inst); 270 Instruction i2 = CondMove.create(moveOp, scalars[0].copyRO(), 271 AStore.getIndex(inst), IC(0), ConditionOperand.EQUAL(), 272 value, scalars[0].copyRO()); 273 DefUse.replaceInstructionAndUpdateDU(inst, i2); 274 Instruction i3 = CondMove.create(moveOp, scalars[1].copyRO(), 275 AStore.getIndex(inst), IC(0), ConditionOperand.NOT_EQUAL(), 276 value, scalars[1].copyRO()); 277 i2.insertAfter(i3); 278 DefUse.updateDUForNewInstruction(i3); 279 } 280 } else { 281 if (size == 1) { 282 int index = 0; 283 Operator moveOp = IRTools.getMoveOp(type.getTypeRef()); 284 Instruction i2 = Move.create(moveOp, scalars[index].copyRO(), AStore.getClearValue(inst)); 285 DefUse.replaceInstructionAndUpdateDU(inst, i2); 286 } else { 287 DefUse.removeInstructionAndUpdateDU(inst); 288 } 289 } 290 } 291 } 292 break; 293 case NULL_CHECK_opcode: { 294 // Null check on result of new array must succeed 295 Instruction i2 = Move.create(GUARD_MOVE, NullCheck.getClearGuardResult(inst), new TrueGuardOperand()); 296 DefUse.replaceInstructionAndUpdateDU(inst, i2); 297 } 298 break; 299 case BOUNDS_CHECK_opcode: { 300 // Remove or create trap as appropriate 301 Instruction i2 = TrapIf.create(TRAP_IF, BoundsCheck.getClearGuardResult(inst), 302 IC(size), BoundsCheck.getClearIndex(inst), ConditionOperand.LOWER_EQUAL(), 303 TrapCodeOperand.ArrayBounds()); 304 DefUse.replaceInstructionAndUpdateDU(inst, i2); 305 } 306 break; 307 case CHECKCAST_opcode: 308 case CHECKCAST_NOTNULL_opcode: 309 case CHECKCAST_UNRESOLVED_opcode: { 310 // We cannot handle removing the checkcast if the result of the 311 // checkcast test is unknown 312 TypeReference lhsType = TypeCheck.getType(inst).getTypeRef(); 313 if (ClassLoaderProxy.includesType(lhsType, vmArray.getTypeRef()) == OptConstants.YES) { 314 if (visited == null) { 315 visited = new HashSet<Register>(); 316 } 317 Register copy = TypeCheck.getResult(inst).getRegister(); 318 if(!visited.contains(copy)) { 319 visited.add(copy); 320 transform2(copy, inst, scalars); 321 // NB will remove inst 322 } else { 323 DefUse.removeInstructionAndUpdateDU(inst); 324 } 325 } else { 326 Instruction i2 = Trap.create(TRAP, null, TrapCodeOperand.CheckCast()); 327 DefUse.replaceInstructionAndUpdateDU(inst, i2); 328 } 329 } 330 break; 331 case INSTANCEOF_opcode: 332 case INSTANCEOF_NOTNULL_opcode: 333 case INSTANCEOF_UNRESOLVED_opcode: { 334 // We cannot handle removing the instanceof if the result of the 335 // instanceof test is unknown 336 TypeReference lhsType = InstanceOf.getType(inst).getTypeRef(); 337 Instruction i2; 338 if (ClassLoaderProxy.includesType(lhsType, vmArray.getTypeRef()) == OptConstants.YES) { 339 i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(1)); 340 } else { 341 i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(0)); 342 } 343 DefUse.replaceInstructionAndUpdateDU(inst, i2); 344 } 345 break; 346 case GET_OBJ_TIB_opcode: { 347 Instruction i2 = Move.create(REF_MOVE, GuardedUnary.getClearResult(inst), new TIBConstantOperand(vmArray)); 348 DefUse.replaceInstructionAndUpdateDU(inst, i2); 349 } 350 break; 351 case REF_MOVE_opcode: { 352 if (visited == null) { 353 visited = new HashSet<Register>(); 354 } 355 Register copy = Move.getResult(inst).getRegister(); 356 if(!visited.contains(copy)) { 357 visited.add(copy); 358 transform2(copy, inst, scalars); 359 // NB will remove inst 360 } else { 361 DefUse.removeInstructionAndUpdateDU(inst); 362 } 363 } 364 break; 365 default: 366 throw new OptimizingCompilerException("Unexpected instruction: " + inst); 367 } 368 } 369 370 /** 371 * Some cases we don't handle yet. TODO: handle them. 372 * 373 * @param ir the governing IR 374 * @param reg the register in question 375 * @param size the size of the array to scalar replace. 376 */ 377 private static boolean containsUnsupportedUse(IR ir, Register reg, int size, RVMArray vmArray, Set<Register> visited) { 378 // If an array is accessed by a non-constant integer, what's the maximum size of support array? 379 final int MAX_SIZE_FOR_VARIABLE_LOAD_STORE = VM.BuildForIA32 ? 2 : 1; 380 for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) { 381 switch (use.instruction.getOpcode()) { 382 case REF_IFCMP_opcode: 383 // Comparison between the array reference we want to replace and 384 // another. TODO: this case is either always true or always false, 385 // we should optimize 386 case NEWOBJMULTIARRAY_opcode: 387 // dimensions array must be passed as an array argument to 388 // newobjmultiarray, common case of 2 arguments is handled without a 389 // dimensions array 390 case OBJARRAY_STORE_CHECK_opcode: 391 case OBJARRAY_STORE_CHECK_NOTNULL_opcode: 392 // TODO: create a store check that doesn't need an array argument 393 return true; 394 case CHECKCAST_opcode: 395 case CHECKCAST_NOTNULL_opcode: 396 case CHECKCAST_UNRESOLVED_opcode: { 397 // We cannot handle removing the checkcast if the result of the 398 // checkcast test is unknown 399 TypeReference lhsType = TypeCheck.getType(use.instruction).getTypeRef(); 400 byte ans = ClassLoaderProxy.includesType(lhsType, vmArray.getTypeRef()); 401 if (ans == OptConstants.MAYBE) { 402 return true; 403 } else if (ans == OptConstants.YES) { 404 // handle as a move 405 if (visited == null) { 406 visited = new HashSet<Register>(); 407 } 408 Register copy = TypeCheck.getResult(use.instruction).getRegister(); 409 if(!visited.contains(copy)) { 410 visited.add(copy); 411 if(containsUnsupportedUse(ir, copy, size, vmArray, visited)) { 412 return true; 413 } 414 } 415 } 416 } 417 break; 418 case INSTANCEOF_opcode: 419 case INSTANCEOF_NOTNULL_opcode: 420 case INSTANCEOF_UNRESOLVED_opcode: { 421 // We cannot handle removing the instanceof if the result of the 422 // instanceof test is unknown 423 TypeReference lhsType = InstanceOf.getType(use.instruction).getTypeRef(); 424 if (ClassLoaderProxy.includesType(lhsType, vmArray.getTypeRef()) == OptConstants.MAYBE) { 425 return true; 426 } 427 } 428 break; 429 case INT_ASTORE_opcode: 430 case LONG_ASTORE_opcode: 431 case FLOAT_ASTORE_opcode: 432 case DOUBLE_ASTORE_opcode: 433 case BYTE_ASTORE_opcode: 434 case SHORT_ASTORE_opcode: 435 case REF_ASTORE_opcode: 436 // Don't handle registers as indexes 437 // TODO: support for registers if the size of the array is small (e.g. 1) 438 if (!AStore.getIndex(use.instruction).isIntConstant() && size > MAX_SIZE_FOR_VARIABLE_LOAD_STORE) { 439 return true; 440 } 441 break; 442 case INT_ALOAD_opcode: 443 case LONG_ALOAD_opcode: 444 case FLOAT_ALOAD_opcode: 445 case DOUBLE_ALOAD_opcode: 446 case BYTE_ALOAD_opcode: 447 case UBYTE_ALOAD_opcode: 448 case USHORT_ALOAD_opcode: 449 case SHORT_ALOAD_opcode: 450 case REF_ALOAD_opcode: 451 // Don't handle registers as indexes 452 // TODO: support for registers if the size of the array is small (e.g. 1) 453 if (!ALoad.getIndex(use.instruction).isIntConstant() && size > MAX_SIZE_FOR_VARIABLE_LOAD_STORE) { 454 return true; 455 } 456 break; 457 case REF_MOVE_opcode: 458 if (visited == null) { 459 visited = new HashSet<Register>(); 460 } 461 Register copy = Move.getResult(use.instruction).getRegister(); 462 if(!visited.contains(copy)) { 463 visited.add(copy); 464 if(containsUnsupportedUse(ir, copy, size, vmArray, visited)) { 465 return true; 466 } 467 } 468 break; 469 } 470 } 471 return false; 472 } 473 }