/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt; import org.jikesrvm.classloader.VM_Array; import org.jikesrvm.classloader.VM_Type; import org.jikesrvm.compilers.opt.ir.ALoad; import org.jikesrvm.compilers.opt.ir.AStore; import org.jikesrvm.compilers.opt.ir.Move; import org.jikesrvm.compilers.opt.ir.NewArray; import org.jikesrvm.compilers.opt.ir.OPT_IR; import org.jikesrvm.compilers.opt.ir.OPT_IRTools; import org.jikesrvm.compilers.opt.ir.OPT_Instruction; import org.jikesrvm.compilers.opt.ir.OPT_Operand; import org.jikesrvm.compilers.opt.ir.OPT_Operator; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BOUNDS_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BYTE_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BYTE_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.DOUBLE_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.DOUBLE_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.FLOAT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.FLOAT_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GET_OBJ_TIB_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INSTANCEOF_NOTNULL_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INSTANCEOF_UNRESOLVED_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INSTANCEOF_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEWARRAY; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NEWOBJMULTIARRAY_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NULL_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.OBJARRAY_STORE_CHECK_NOTNULL_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.OBJARRAY_STORE_CHECK_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.SHORT_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.SHORT_ASTORE_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.UBYTE_ALOAD_opcode; import static org.jikesrvm.compilers.opt.ir.OPT_Operators.USHORT_ALOAD_opcode; import org.jikesrvm.compilers.opt.ir.OPT_Register; import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand; /** * Class that performs scalar replacement of short arrays */ public final class OPT_ShortArrayReplacer implements OPT_AggregateReplacer { private static final boolean DEBUG = false; /** * Arrays shorter than this length are candidates to be replaced by * scalar values. */ public static final int SHORT_ARRAY_SIZE = 5; /** * Return an object representing this transformation for a given * allocation site * * @param inst the allocation site * @param ir * @return the object, or null if illegal */ public static OPT_ShortArrayReplacer getReplacer(OPT_Instruction inst, OPT_IR ir) { if (inst.operator != NEWARRAY) { return null; } OPT_Operand size = NewArray.getSize(inst); if (!size.isIntConstant()) { return null; } int s = size.asIntConstant().value; if (s > SHORT_ARRAY_SIZE) { return null; } if (s < 0) { return null; } OPT_Register r = NewArray.getResult(inst).getRegister(); VM_Array a = NewArray.getType(inst).getVMType().asArray(); // TODO :handle these cases if (containsUnsupportedUse(ir, r, s)) { return null; } return new OPT_ShortArrayReplacer(r, a, s, ir); } /** * Perform the transformation. */ public void transform() { // first set up temporary scalars for the array elements // initialize them before the def. OPT_RegisterOperand[] scalars = new OPT_RegisterOperand[size]; VM_Type elementType = vmArray.getElementType(); OPT_RegisterOperand def = reg.defList; OPT_Instruction defI = def.instruction; OPT_Operand defaultValue = OPT_IRTools.getDefaultOperand(elementType.getTypeRef()); for (int i = 0; i < size; i++) { scalars[i] = OPT_IRTools.moveIntoRegister(ir.regpool, defI, defaultValue.copy()); } // now remove the def if (DEBUG) { System.out.println("Removing " + defI); } OPT_DefUse.removeInstructionAndUpdateDU(defI); // now handle the uses for (OPT_RegisterOperand use = reg.useList; use != null; use = use.getNext()) { scalarReplace(use, scalars); } } /** * number of elements in the array */ private int size; /** * type of the array */ private VM_Array vmArray; /** * the register holding the array reference */ private OPT_Register reg; /** * the governing IR */ private OPT_IR ir; /** * @param r the register holding the array reference * @param a the type of the array to replace * @param s the size of the array to replace * @param i the IR */ private OPT_ShortArrayReplacer(OPT_Register r, VM_Array a, int s, OPT_IR i) { reg = r; vmArray = a; size = s; ir = i; } /** * Replace a given use of an array with its scalar equivalent. * * @param use the use to replace * @param scalars an array of scalar register operands to replace * the array with */ private void scalarReplace(OPT_RegisterOperand use, OPT_RegisterOperand[] scalars) { OPT_Instruction inst = use.instruction; VM_Type type = vmArray.getElementType(); OPT_Operator moveOp = OPT_IRTools.getMoveOp(type.getTypeRef()); switch (inst.getOpcode()) { case INT_ALOAD_opcode: case LONG_ALOAD_opcode: case FLOAT_ALOAD_opcode: case DOUBLE_ALOAD_opcode: case BYTE_ALOAD_opcode: case UBYTE_ALOAD_opcode: case USHORT_ALOAD_opcode: case SHORT_ALOAD_opcode: case REF_ALOAD_opcode: { int index = ALoad.getIndex(inst).asIntConstant().value; OPT_Instruction i = Move.create(moveOp, ALoad.getClearResult(inst), scalars[index].copyRO()); inst.insertBefore(i); OPT_DefUse.removeInstructionAndUpdateDU(inst); OPT_DefUse.updateDUForNewInstruction(i); } break; case INT_ASTORE_opcode: case LONG_ASTORE_opcode: case FLOAT_ASTORE_opcode: case DOUBLE_ASTORE_opcode: case BYTE_ASTORE_opcode: case SHORT_ASTORE_opcode: case REF_ASTORE_opcode: { int index = AStore.getIndex(inst).asIntConstant().value; OPT_Instruction i2 = Move.create(moveOp, scalars[index].copyRO(), AStore.getClearValue(inst)); inst.insertBefore(i2); OPT_DefUse.removeInstructionAndUpdateDU(inst); OPT_DefUse.updateDUForNewInstruction(i2); } break; case BOUNDS_CHECK_opcode: OPT_DefUse.removeInstructionAndUpdateDU(inst); break; default: throw new OPT_OptimizingCompilerException("Unexpected instruction: " + inst); } } /** * Some cases we don't handle yet. TODO: handle them. * * @param ir the governing IR * @param reg the register in question * @param size the size of the array to scalar replace. */ private static boolean containsUnsupportedUse(OPT_IR ir, OPT_Register reg, int size) { for (OPT_RegisterOperand use = reg.useList; use != null; use = use.getNext()) { switch (use.instruction.getOpcode()) { case NEWOBJMULTIARRAY_opcode: case OBJARRAY_STORE_CHECK_opcode: case OBJARRAY_STORE_CHECK_NOTNULL_opcode: case GET_OBJ_TIB_opcode: case NULL_CHECK_opcode: case INSTANCEOF_opcode: case INSTANCEOF_NOTNULL_opcode: case INSTANCEOF_UNRESOLVED_opcode: return true; case INT_ASTORE_opcode: case LONG_ASTORE_opcode: case FLOAT_ASTORE_opcode: case DOUBLE_ASTORE_opcode: case BYTE_ASTORE_opcode: case SHORT_ASTORE_opcode: case REF_ASTORE_opcode: { if (!AStore.getIndex(use.instruction).isIntConstant()) { return true; } int index = AStore.getIndex(use.instruction).asIntConstant().value; // In the following case, we could instead unconditionally throw // an array index out-of-bounds exception. if (index >= size) return true; if (index < 0) return true; break; } case INT_ALOAD_opcode: case LONG_ALOAD_opcode: case FLOAT_ALOAD_opcode: case DOUBLE_ALOAD_opcode: case BYTE_ALOAD_opcode: case UBYTE_ALOAD_opcode: case USHORT_ALOAD_opcode: case SHORT_ALOAD_opcode: case REF_ALOAD_opcode: { if (!ALoad.getIndex(use.instruction).isIntConstant()) { return true; } int index = ALoad.getIndex(use.instruction).asIntConstant().value; // In the following case, we could instead unconditionally throw // an array index out-of-bounds exception. if (index >= size) return true; if (index < 0) return true; break; } } } return false; } }