/*
* 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 java.util.ArrayList;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.VM_Class;
import org.jikesrvm.classloader.VM_Field;
import org.jikesrvm.classloader.VM_FieldReference;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.compilers.opt.ir.Empty;
import org.jikesrvm.compilers.opt.ir.GetField;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.New;
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.BOOLEAN_CMP_ADDR_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BOOLEAN_CMP_INT_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.CHECKCAST_NOTNULL_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.CHECKCAST_UNRESOLVED_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.CHECKCAST_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GETFIELD_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.LONG_STORE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.MONITORENTER_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.MONITOREXIT_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.MUST_IMPLEMENT_INTERFACE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.NULL_CHECK_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PUTFIELD_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.READ_CEILING;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_IFCMP_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.WRITE_FLOOR;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
import org.jikesrvm.compilers.opt.ir.PutField;
//TODO - Deal with Subarch
/**
* Class that performs scalar replacement of aggregates for non-array
* objects
*/
public final class OPT_ObjectReplacer implements OPT_AggregateReplacer {
static final boolean DEBUG = false;
/**
* 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_ObjectReplacer getReplacer(OPT_Instruction inst, OPT_IR ir) {
OPT_Register r = New.getResult(inst).getRegister();
// TODO :handle these cases
if (containsUnsupportedUse(ir, r)) {
return null;
}
VM_Class klass = New.getType(inst).getVMType().asClass();
return new OPT_ObjectReplacer(r, klass, ir);
}
/**
* Perform the transformation
*/
public void transform() {
// store the object's fields in a ArrayList
ArrayList<VM_Field> fields = getFieldsAsArrayList(klass);
// create a scalar for each field. initialize the scalar to
// default values before the object's def
OPT_RegisterOperand[] scalars = new OPT_RegisterOperand[fields.size()];
OPT_RegisterOperand def = reg.defList;
OPT_Instruction defI = def.instruction;
for (int i = 0; i < fields.size(); i++) {
VM_Field f = fields.get(i);
OPT_Operand defaultValue = OPT_IRTools.getDefaultOperand(f.getType());
scalars[i] = OPT_IRTools.moveIntoRegister(ir.regpool, defI, defaultValue);
scalars[i].setType(f.getType());
}
// 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, fields);
}
}
/**
* type of the object
*/
private final VM_Class klass;
/**
* the IR
*/
private final OPT_IR ir;
/**
* the register holding the object reference
*/
private final OPT_Register reg;
/**
* Returns a ArrayList<VM_Field>, holding the fields of the object
* @param klass the type of the object
*/
private static ArrayList<VM_Field> getFieldsAsArrayList(VM_Class klass) {
ArrayList<VM_Field> v = new ArrayList<VM_Field>();
for (VM_Field field : klass.getInstanceFields()) {
v.add(field);
}
return v;
}
/**
* @param r the register holding the object reference
* @param _klass the type of the object to replace
* @param i the IR
*/
private OPT_ObjectReplacer(OPT_Register r, VM_Class _klass, OPT_IR i) {
reg = r;
klass = _klass;
ir = i;
}
/**
* Replace a given use of a object with its scalar equivalent
*
* @param use the use to replace
* @param scalars an array of scalar register operands to replace
* the object's fields with
*/
private void scalarReplace(OPT_RegisterOperand use, OPT_RegisterOperand[] scalars, ArrayList<VM_Field> fields) {
OPT_Instruction inst = use.instruction;
switch (inst.getOpcode()) {
case PUTFIELD_opcode: {
VM_FieldReference fr = PutField.getLocation(inst).getFieldRef();
if (VM.VerifyAssertions) VM._assert(fr.isResolved());
VM_Field f = fr.peekResolvedField(false);
int index = fields.indexOf(f);
VM_TypeReference type = scalars[index].getType();
OPT_Operator moveOp = OPT_IRTools.getMoveOp(type);
OPT_Instruction i = Move.create(moveOp, scalars[index].copyRO(), PutField.getClearValue(inst));
inst.insertBefore(i);
OPT_DefUse.removeInstructionAndUpdateDU(inst);
OPT_DefUse.updateDUForNewInstruction(i);
}
break;
case GETFIELD_opcode: {
VM_FieldReference fr = GetField.getLocation(inst).getFieldRef();
if (VM.VerifyAssertions) VM._assert(fr.isResolved());
VM_Field f = fr.peekResolvedField(false);
int index = fields.indexOf(f);
VM_TypeReference type = scalars[index].getType();
OPT_Operator moveOp = OPT_IRTools.getMoveOp(type);
OPT_Instruction i = Move.create(moveOp, GetField.getClearResult(inst), scalars[index].copyRO());
inst.insertBefore(i);
OPT_DefUse.removeInstructionAndUpdateDU(inst);
OPT_DefUse.updateDUForNewInstruction(i);
}
break;
case MONITORENTER_opcode:
if (ir.options.NO_CACHE_FLUSH) {
OPT_DefUse.removeInstructionAndUpdateDU(inst);
} else {
inst.insertBefore(Empty.create(READ_CEILING));
OPT_DefUse.removeInstructionAndUpdateDU(inst);
}
break;
case MONITOREXIT_opcode:
if (ir.options.NO_CACHE_FLUSH) {
OPT_DefUse.removeInstructionAndUpdateDU(inst);
} else {
inst.insertBefore(Empty.create(WRITE_FLOOR));
OPT_DefUse.removeInstructionAndUpdateDU(inst);
}
break;
case NULL_CHECK_opcode:
// (SJF) TODO: Why wasn't this caught by BC2IR for
// java.lang.Double.<init> (Ljava/lang/String;)V ?
OPT_DefUse.removeInstructionAndUpdateDU(inst);
break;
default:
throw new OPT_OptimizingCompilerException("OPT_ObjectReplacer: unexpected use " + inst);
}
}
/**
* Some cases we don't handle yet. TODO: handle them.
*/
private static boolean containsUnsupportedUse(OPT_IR ir, OPT_Register reg) {
for (OPT_RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
switch (use.instruction.getOpcode()) {
case CHECKCAST_opcode:
case CHECKCAST_UNRESOLVED_opcode:
case MUST_IMPLEMENT_INTERFACE_opcode:
case CHECKCAST_NOTNULL_opcode:
case GET_OBJ_TIB_opcode:
case INSTANCEOF_opcode:
case INSTANCEOF_NOTNULL_opcode:
case INSTANCEOF_UNRESOLVED_opcode:
case REF_IFCMP_opcode:
case BOOLEAN_CMP_INT_opcode:
case BOOLEAN_CMP_ADDR_opcode:
case LONG_STORE_opcode:
return true;
}
}
return false;
}
}