/*
* 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.lang.reflect.Constructor;
import java.util.ArrayList;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.ir.Binary;
import org.jikesrvm.compilers.opt.ir.BoundsCheck;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.GetField;
import org.jikesrvm.compilers.opt.ir.GetStatic;
import org.jikesrvm.compilers.opt.ir.GuardResultCarrier;
import org.jikesrvm.compilers.opt.ir.GuardedBinary;
import org.jikesrvm.compilers.opt.ir.GuardedUnary;
import org.jikesrvm.compilers.opt.ir.InstanceOf;
import org.jikesrvm.compilers.opt.ir.LocationCarrier;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.NullCheck;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
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_InstructionFormat;
import org.jikesrvm.compilers.opt.ir.OPT_IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_LocationOperand;
import org.jikesrvm.compilers.opt.ir.OPT_Operand;
import org.jikesrvm.compilers.opt.ir.OPT_OperandEnumeration;
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.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_ZERO_CHECK_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_ZERO_CHECK_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.NULL_CHECK_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.READ_CEILING_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.TRAP_IF_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.WRITE_FLOOR_opcode;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
import org.jikesrvm.compilers.opt.ir.OPT_TrapCodeOperand;
import org.jikesrvm.compilers.opt.ir.PutField;
import org.jikesrvm.compilers.opt.ir.PutStatic;
import org.jikesrvm.compilers.opt.ir.ResultCarrier;
import org.jikesrvm.compilers.opt.ir.TrapIf;
import org.jikesrvm.compilers.opt.ir.TypeCheck;
import org.jikesrvm.compilers.opt.ir.Unary;
import org.jikesrvm.compilers.opt.ir.ZeroCheck;
/**
* Perform local common-subexpression elimination for a factored basic
* block.
* <ul>
* <li> Note: this module also performs scalar replacement of loads
* <li> Note: this module also performs elimination of redundant
* nullchecks, boundchecks, and zero checks.
* </ul>
* Algorithm: Muchnick pp.379-385
*
* partially based on OPT_LocalBoundsCheck)
*/
public class OPT_LocalCSE extends OPT_CompilerPhase {
private final boolean isHIR;
/**
* Constructor
*/
public OPT_LocalCSE(boolean isHIR) {
super(new Object[]{isHIR});
this.isHIR = isHIR;
}
/**
* Constructor for this compiler phase
*/
private static final Constructor<OPT_CompilerPhase> constructor =
getCompilerPhaseConstructor(OPT_LocalCSE.class, new Class[]{Boolean.TYPE});
/**
* Get a constructor object for this compiler phase
* @return compiler phase constructor
*/
public Constructor<OPT_CompilerPhase> getClassConstructor() {
return constructor;
}
public final void reportAdditionalStats() {
VM.sysWrite(" ");
VM.sysWrite(container.counter1 / container.counter2 * 100, 2);
VM.sysWrite("% Infrequent BBs");
}
public final boolean shouldPerform(OPT_Options options) {
return options.LOCAL_CSE;
}
public final String getName() {
return "Local CSE";
}
/**
* Perform Local CSE for a method.
*
* @param ir the IR to optimize
*/
public final void perform(OPT_IR ir) {
// iterate over each basic block
for (OPT_BasicBlock bb = ir.firstBasicBlockInCodeOrder(); bb != null; bb = bb.nextBasicBlockInCodeOrder()) {
if (bb.isEmpty()) continue;
container.counter2++;
if (bb.getInfrequent()) {
container.counter1++;
if (ir.options.FREQ_FOCUS_EFFORT) continue;
}
if (isHIR) {
optimizeBasicBlockHIR(ir, bb);
} else {
optimizeBasicBlockLIR(ir, bb);
}
}
}
/**
* Perform Local CSE for a basic block in HIR.
*
* @param ir the method's ir
* @param bb the basic block
*/
private void optimizeBasicBlockHIR(OPT_IR ir, OPT_BasicBlock bb) {
AvExCache cache = new AvExCache(ir.options, true);
// iterate over all instructions in the basic block
for (OPT_Instruction inst = bb.firstRealInstruction(),
sentinel = bb.lastInstruction(),
nextInstr = null; inst != sentinel; inst = nextInstr) {
nextInstr = inst.nextInstructionInCodeOrder(); // cache before we
// mutate prev/next links
// 1. try and replace this instruction according to
// available expressions in the cache, and update cache
// accordingly.
if (isLoadInstruction(inst)) {
loadHelper(ir, cache, inst);
} else if (isStoreInstruction(inst)) {
storeHelper(cache, inst);
} else if (isExpression(inst)) {
expressionHelper(ir, cache, inst);
} else if (isCheck(inst)) {
checkHelper(ir, cache, inst);
} else if (isTypeCheck(inst)) {
typeCheckHelper(ir, cache, inst);
}
// 2. update the cache according to which expressions this
// instruction kills
cache.eliminate(inst);
// CALL instructions and synchronizations KILL all memory locations!
if (Call.conforms(inst) || isSynchronizing(inst) || inst.isDynamicLinkingPoint()) {
cache.invalidateAllLoads();
}
}
}
/**
* Perform Local CSE for a basic block in LIR.
*
* @param ir the method's ir
* @param bb the basic block
*/
private void optimizeBasicBlockLIR(OPT_IR ir, OPT_BasicBlock bb) {
AvExCache cache = new AvExCache(ir.options, false);
// iterate over all instructions in the basic block
for (OPT_Instruction inst = bb.firstRealInstruction(),
sentinel = bb.lastInstruction(),
nextInstr = null; inst != sentinel; inst = nextInstr) {
nextInstr = inst.nextInstructionInCodeOrder(); // cache before we
// mutate prev/next links
// 1. try and replace this instruction according to
// available expressions in the cache, and update cache
// accordingly.
if (isExpression(inst)) {
expressionHelper(ir, cache, inst);
} else if (isCheck(inst)) {
checkHelper(ir, cache, inst);
}
// 2. update the cache according to which expressions this
// instruction kills
cache.eliminate(inst);
}
}
/**
* Is a given instruction a CSE-able load?
*/
public static boolean isLoadInstruction(OPT_Instruction s) {
return GetField.conforms(s) || GetStatic.conforms(s);
}
/**
* Is a given instruction a CSE-able store?
*/
public static boolean isStoreInstruction(OPT_Instruction s) {
return PutField.conforms(s) || PutStatic.conforms(s);
}
/**
* Does the instruction compute some expression?
*
* @param inst the instruction in question
* @return true or false, as appropriate
*/
private boolean isExpression(OPT_Instruction inst) {
if (inst.isDynamicLinkingPoint()) return false;
switch (inst.operator.format) {
case OPT_InstructionFormat.Unary_format:
case OPT_InstructionFormat.GuardedUnary_format:
case OPT_InstructionFormat.Binary_format:
case OPT_InstructionFormat.GuardedBinary_format:
case OPT_InstructionFormat.InstanceOf_format:
return true;
default:
return false;
}
}
/**
* Is the given instruction a check instruction?
*
* @param inst the instruction in question
* @return true or false, as appropriate
*/
private boolean isCheck(OPT_Instruction inst) {
switch (inst.getOpcode()) {
case NULL_CHECK_opcode:
case BOUNDS_CHECK_opcode:
case INT_ZERO_CHECK_opcode:
case LONG_ZERO_CHECK_opcode:
return true;
case TRAP_IF_opcode:
OPT_TrapCodeOperand tc = TrapIf.getTCode(inst);
return tc.isNullPtr() || tc.isArrayBounds() || tc.isDivByZero();
default:
return false;
}
}
private boolean isTypeCheck(OPT_Instruction inst) {
return TypeCheck.conforms(inst);
}
/**
* Process a load instruction
*
* @param ir the containing IR object.
* @param cache the cache of available expressions
* @param inst the instruction begin processed
*/
private void loadHelper(OPT_IR ir, AvExCache cache, OPT_Instruction inst) {
OPT_LocationOperand loc = LocationCarrier.getLocation(inst);
if (loc.mayBeVolatile()) return; // don't optimize volatile fields
// look up the expression in the cache
AvailableExpression ae = cache.find(inst);
if (ae != null) {
OPT_RegisterOperand dest = ResultCarrier.getClearResult(inst);
if (ae.tmp == null) {
// (1) generate a new temporary, and store in the AE cache
OPT_RegisterOperand newRes = ir.regpool.makeTemp(dest.getType());
ae.tmp = newRes.getRegister();
// (2) get the CSE value into newRes
if (ae.isLoad()) {
// the first appearance was a load.
// Modify the first load to assign its result to a new temporary
// and then insert a move from the new temporary to the old result
// after the mutated first load.
OPT_RegisterOperand res = ResultCarrier.getClearResult(ae.inst);
ResultCarrier.setResult(ae.inst, newRes);
ae.inst.insertAfter(Move.create(getMoveOp(res), res, newRes.copyD2U()));
} else {
// the first appearance was a store.
// Insert a move that assigns the value to newRes before
// the store instruction.
OPT_Operand value;
if (PutStatic.conforms(ae.inst)) {
value = PutStatic.getValue(ae.inst);
} else {
value = PutField.getValue(ae.inst);
}
ae.inst.insertBefore(Move.create(getMoveOp(newRes), newRes, value.copy()));
}
// (3) replace second load with a move from the new temporary
Move.mutate(inst, getMoveOp(dest), dest, newRes.copyD2U());
} else {
// already have a temp. replace the load with a move
OPT_RegisterOperand newRes = new OPT_RegisterOperand(ae.tmp, dest.getType());
Move.mutate(inst, getMoveOp(dest), dest, newRes);
}
} else {
// did not find a match: insert new entry in cache
cache.insert(inst);
}
}
/**
* Process a store instruction
*
* @param cache the cache of available expressions
* @param inst the instruction begin processed
*/
private void storeHelper(AvExCache cache, OPT_Instruction inst) {
OPT_LocationOperand loc = LocationCarrier.getLocation(inst);
if (loc.mayBeVolatile()) return; // don't optimize volatile fields
// look up the expression in the cache
AvailableExpression ae = cache.find(inst);
if (ae == null) {
// did not find a match: insert new entry in cache
cache.insert(inst);
}
}
/**
* Process a unary or binary expression.
*
* @param ir the containing IR object
* @param cache the cache of available expressions
* @param inst the instruction begin processed
*/
private void expressionHelper(OPT_IR ir, AvExCache cache, OPT_Instruction inst) {
// look up the expression in the cache
AvailableExpression ae = cache.find(inst);
if (ae != null) {
OPT_RegisterOperand dest = ResultCarrier.getClearResult(inst);
if (ae.tmp == null) {
// (1) generate a new temporary, and store in the AE cache
OPT_RegisterOperand newRes = ir.regpool.makeTemp(dest.getType());
ae.tmp = newRes.getRegister();
// (2) Modify ae.inst to assign its result to the new temporary
// and then insert a move from the new temporary to the old result
// of ae.inst after ae.inst.
OPT_RegisterOperand res = ResultCarrier.getClearResult(ae.inst);
ResultCarrier.setResult(ae.inst, newRes);
ae.inst.insertAfter(Move.create(getMoveOp(res), res, newRes.copyD2U()));
// (3) replace inst with a move from the new temporary
Move.mutate(inst, getMoveOp(dest), dest, newRes.copyD2U());
} else {
// already have a temp. replace inst with a move
OPT_RegisterOperand newRes = new OPT_RegisterOperand(ae.tmp, dest.getType());
Move.mutate(inst, getMoveOp(dest), dest, newRes);
}
} else {
// did not find a match: insert new entry in cache
cache.insert(inst);
}
}
/**
* Process a check instruction
*
* @param cache the cache of available expressions
* @param inst the instruction begin processed
*/
private void checkHelper(OPT_IR ir, AvExCache cache, OPT_Instruction inst) {
// look up the check in the cache
AvailableExpression ae = cache.find(inst);
if (ae != null) {
OPT_RegisterOperand dest = GuardResultCarrier.getClearGuardResult(inst);
if (ae.tmp == null) {
// generate a new temporary, and store in the AE cache
OPT_RegisterOperand newRes = ir.regpool.makeTemp(dest.getType());
ae.tmp = newRes.getRegister();
// (2) Modify ae.inst to assign its guard result to the new temporary
// and then insert a guard move from the new temporary to the
// old guard result of ae.inst after ae.inst.
OPT_RegisterOperand res = GuardResultCarrier.getClearGuardResult(ae.inst);
GuardResultCarrier.setGuardResult(ae.inst, newRes);
ae.inst.insertAfter(Move.create(GUARD_MOVE, res, newRes.copyD2U()));
// (3) replace inst with a move from the new temporary
Move.mutate(inst, GUARD_MOVE, dest, newRes.copyD2U());
} else {
// already have a temp. replace inst with a guard move
OPT_RegisterOperand newRes = new OPT_RegisterOperand(ae.tmp, dest.getType());
Move.mutate(inst, GUARD_MOVE, dest, newRes);
}
} else {
// did not find a match: insert new entry in cache
cache.insert(inst);
}
}
/**
* Process a type check instruction
*
* @param ir Unused
* @param cache The cache of available expressions.
* @param inst The instruction being processed
*/
private void typeCheckHelper(OPT_IR ir, AvExCache cache, OPT_Instruction inst) {
// look up the check in the cache
AvailableExpression ae = cache.find(inst);
if (ae != null) {
// it's a duplicate; blow it away.
inst.remove();
} else {
// did not find a match: insert new entry in cache
cache.insert(inst);
}
}
private OPT_Operator getMoveOp(OPT_RegisterOperand r) {
return OPT_IRTools.getMoveOp(r.getType());
}
/**
* Is this a synchronizing instruction?
*
* @param inst the instruction in question
*/
private static boolean isSynchronizing(OPT_Instruction inst) {
switch (inst.getOpcode()) {
case MONITORENTER_opcode:
case MONITOREXIT_opcode:
case READ_CEILING_opcode:
case WRITE_FLOOR_opcode:
return true;
default:
return false;
}
}
/**
* Implements a cache of Available Expressions
*/
protected static final class AvExCache {
/** Implementation of the cache */
private final ArrayList<AvailableExpression> cache = new ArrayList<AvailableExpression>(3);
private final OPT_Options options;
private final boolean doMemory;
AvExCache(OPT_Options opts, boolean doMem) {
options = opts;
doMemory = doMem;
}
/**
* Find and return a matching available expression.
*
* @param inst the instruction to match
* @return the matching AE if found, null otherwise
*/
public AvailableExpression find(OPT_Instruction inst) {
OPT_Operator opr = inst.operator();
OPT_Operand op1 = null;
OPT_Operand op2 = null;
OPT_Operand op3 = null;
OPT_LocationOperand location = null;
switch (inst.operator.format) {
case OPT_InstructionFormat.GetField_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
op1 = GetField.getRef(inst);
location = GetField.getLocation(inst);
break;
case OPT_InstructionFormat.GetStatic_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
location = GetStatic.getLocation(inst);
break;
case OPT_InstructionFormat.PutField_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
op1 = PutField.getRef(inst);
location = PutField.getLocation(inst);
break;
case OPT_InstructionFormat.PutStatic_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
location = PutStatic.getLocation(inst);
break;
case OPT_InstructionFormat.Unary_format:
op1 = Unary.getVal(inst);
break;
case OPT_InstructionFormat.GuardedUnary_format:
op1 = GuardedUnary.getVal(inst);
break;
case OPT_InstructionFormat.Binary_format:
op1 = Binary.getVal1(inst);
op2 = Binary.getVal2(inst);
break;
case OPT_InstructionFormat.GuardedBinary_format:
op1 = GuardedBinary.getVal1(inst);
op2 = GuardedBinary.getVal2(inst);
break;
case OPT_InstructionFormat.Move_format:
op1 = Move.getVal(inst);
break;
case OPT_InstructionFormat.NullCheck_format:
op1 = NullCheck.getRef(inst);
break;
case OPT_InstructionFormat.ZeroCheck_format:
op1 = ZeroCheck.getValue(inst);
break;
case OPT_InstructionFormat.BoundsCheck_format:
op1 = BoundsCheck.getRef(inst);
op2 = BoundsCheck.getIndex(inst);
break;
case OPT_InstructionFormat.TrapIf_format:
op1 = TrapIf.getVal1(inst);
op2 = TrapIf.getVal2(inst);
op3 = TrapIf.getTCode(inst);
break;
case OPT_InstructionFormat.TypeCheck_format:
op1 = TypeCheck.getRef(inst);
op2 = TypeCheck.getType(inst);
break;
case OPT_InstructionFormat.InstanceOf_format:
op1 = InstanceOf.getRef(inst);
op2 = InstanceOf.getType(inst);
break;
default:
throw new OPT_OptimizingCompilerException("Unsupported type " + inst);
}
AvailableExpression ae = new AvailableExpression(inst, opr, op1, op2, op3, location, null);
int index = cache.indexOf(ae);
if (index == -1) {
return null;
}
return cache.get(index);
}
/**
* Insert a new available expression in the cache
*
* @param inst the instruction that defines the AE
*/
public void insert(OPT_Instruction inst) {
OPT_Operator opr = inst.operator();
OPT_Operand op1 = null;
OPT_Operand op2 = null;
OPT_Operand op3 = null;
OPT_LocationOperand location = null;
switch (inst.operator.format) {
case OPT_InstructionFormat.GetField_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
op1 = GetField.getRef(inst);
location = GetField.getLocation(inst);
break;
case OPT_InstructionFormat.GetStatic_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
location = GetStatic.getLocation(inst);
break;
case OPT_InstructionFormat.PutField_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
op1 = PutField.getRef(inst);
location = PutField.getLocation(inst);
break;
case OPT_InstructionFormat.PutStatic_format:
if (VM.VerifyAssertions) VM._assert(doMemory);
location = PutStatic.getLocation(inst);
break;
case OPT_InstructionFormat.Unary_format:
op1 = Unary.getVal(inst);
break;
case OPT_InstructionFormat.GuardedUnary_format:
op1 = GuardedUnary.getVal(inst);
break;
case OPT_InstructionFormat.Binary_format:
op1 = Binary.getVal1(inst);
op2 = Binary.getVal2(inst);
break;
case OPT_InstructionFormat.GuardedBinary_format:
op1 = GuardedBinary.getVal1(inst);
op2 = GuardedBinary.getVal2(inst);
break;
case OPT_InstructionFormat.Move_format:
op1 = Move.getVal(inst);
break;
case OPT_InstructionFormat.NullCheck_format:
op1 = NullCheck.getRef(inst);
break;
case OPT_InstructionFormat.ZeroCheck_format:
op1 = ZeroCheck.getValue(inst);
break;
case OPT_InstructionFormat.BoundsCheck_format:
op1 = BoundsCheck.getRef(inst);
op2 = BoundsCheck.getIndex(inst);
break;
case OPT_InstructionFormat.TrapIf_format:
op1 = TrapIf.getVal1(inst);
op2 = TrapIf.getVal2(inst);
op3 = TrapIf.getTCode(inst);
break;
case OPT_InstructionFormat.TypeCheck_format:
op1 = TypeCheck.getRef(inst);
op2 = TypeCheck.getType(inst);
break;
case OPT_InstructionFormat.InstanceOf_format:
op1 = InstanceOf.getRef(inst);
op2 = InstanceOf.getType(inst);
break;
default:
throw new OPT_OptimizingCompilerException("Unsupported type " + inst);
}
AvailableExpression ae = new AvailableExpression(inst, opr, op1, op2, op3, location, null);
cache.add(ae);
}
/**
* Eliminate all AE tuples that contain a given operand
*
* @param op the operand in question
*/
private void eliminate(OPT_RegisterOperand op) {
int i = 0;
while (i < cache.size()) {
AvailableExpression ae = cache.get(i);
OPT_Operand opx = ae.op1;
if (opx instanceof OPT_RegisterOperand && ((OPT_RegisterOperand) opx).getRegister() == op.getRegister()) {
cache.remove(i);
continue; // don't increment i, since we removed
}
opx = ae.op2;
if (opx instanceof OPT_RegisterOperand && ((OPT_RegisterOperand) opx).getRegister() == op.getRegister()) {
cache.remove(i);
continue; // don't increment i, since we removed
}
opx = ae.op3;
if (opx instanceof OPT_RegisterOperand && ((OPT_RegisterOperand) opx).getRegister() == op.getRegister()) {
cache.remove(i);
continue; // don't increment i, since we removed
}
i++;
}
}
/**
* Eliminate all AE tuples that are killed by a given instruction
*
* @param s the store instruction
*/
public void eliminate(OPT_Instruction s) {
int i = 0;
// first kill all registers that this instruction defs
for (OPT_OperandEnumeration defs = s.getDefs(); defs.hasMoreElements();) {
// first KILL any registers this instruction DEFS
OPT_Operand def = defs.next();
if (def instanceof OPT_RegisterOperand) {
eliminate((OPT_RegisterOperand) def);
}
}
if (doMemory) {
// eliminate all memory locations killed by stores
if (OPT_LocalCSE.isStoreInstruction(s) || (options.READS_KILL && OPT_LocalCSE.isLoadInstruction(s))) {
// sLocation holds the location killed by this instruction
OPT_LocationOperand sLocation = LocationCarrier.getLocation(s);
// walk through the cache and invalidate any killed locations
while (i < cache.size()) {
AvailableExpression ae = cache.get(i);
if (ae.inst != s) { // a store instruction doesn't kill itself
boolean killIt = false;
if (ae.isLoadOrStore()) {
if ((sLocation == null) && (ae.location == null)) {
// !TODO: is this too conservative??
killIt = true;
} else if ((sLocation != null) && (ae.location != null)) {
killIt = OPT_LocationOperand.mayBeAliased(sLocation, ae.location);
}
}
if (killIt) {
cache.remove(i);
continue; // don't increment i, since we removed
}
}
i++;
}
}
}
}
/**
* Eliminate all AE tuples that cache ANY memory location.
*/
public void invalidateAllLoads() {
if (!doMemory) return;
int i = 0;
while (i < cache.size()) {
AvailableExpression ae = cache.get(i);
if (ae.isLoadOrStore()) {
cache.remove(i);
continue; // don't increment i, since we removed
}
i++;
}
}
}
/**
* A tuple to record an Available Expression
*/
private static final class AvailableExpression {
/**
* the instruction which makes this expression available
*/
final OPT_Instruction inst;
/**
* the operator of the expression
*/
final OPT_Operator opr;
/**
* first operand
*/
final OPT_Operand op1;
/**
* second operand
*/
final OPT_Operand op2;
/**
* third operand
*/
final OPT_Operand op3;
/**
* location operand for memory (load/store) expressions
*/
final OPT_LocationOperand location;
/**
* temporary register holding the result of the available
* expression
*/
OPT_Register tmp;
/**
* @param i the instruction which makes this expression available
* @param op the operator of the expression
* @param o1 first operand
* @param o2 second operand
* @param o3 third operand
* @param loc location operand for memory (load/store) expressions
* @param t temporary register holding the result of the available
* expression
*/
AvailableExpression(OPT_Instruction i, OPT_Operator op, OPT_Operand o1, OPT_Operand o2, OPT_Operand o3,
OPT_LocationOperand loc, OPT_Register t) {
inst = i;
opr = op;
op1 = o1;
op2 = o2;
op3 = o3;
location = loc;
tmp = t;
}
/**
* Two AEs are "equal" iff
* <ul>
* <li> for unary, binary and ternary expressions:
* the operator and the operands match
* <li> for loads and stores: if the 2 operands and the location match
* </ul>
*/
public boolean equals(Object o) {
if (!(o instanceof AvailableExpression)) {
return false;
}
AvailableExpression ae = (AvailableExpression) o;
if (isLoadOrStore()) {
if (!ae.isLoadOrStore()) {
return false;
}
boolean result = OPT_LocationOperand.mayBeAliased(location, ae.location);
if (op1 != null) {
result = result && op1.similar(ae.op1);
} else {
/* op1 is null, so ae.op1 must also be null */
if (ae.op1 != null) {
return false;
}
}
if (op2 != null) {
result = result && op2.similar(ae.op2);
} else {
/* op2 is null, so ae.op2 must also be null */
if (ae.op2 != null) {
return false;
}
}
return result;
} else if (isBoundsCheck()) {
// Augment equality with BC(ref,C1) ==> BC(ref,C2)
// when C1>0, C2>=0, and C1>C2
if (!opr.equals(ae.opr)) {
return false;
}
if (!op1.similar(ae.op1)) {
return false;
}
if (op2.similar(ae.op2)) {
return true;
}
if (op2 instanceof OPT_IntConstantOperand && ae.op2 instanceof OPT_IntConstantOperand) {
int C1 = ((OPT_IntConstantOperand) op2).value;
int C2 = ((OPT_IntConstantOperand) ae.op2).value;
return C1 > 0 && C2 >= 0 && C1 > C2;
} else {
return false;
}
} else {
if (!opr.equals(ae.opr)) {
return false;
}
if (isTernary() && !op3.similar(ae.op3)) {
return false;
}
if (!isBinary()) {
return op1.similar(ae.op1);
}
if (op1.similar(ae.op1) && op2.similar(ae.op2)) {
return true;
}
return isCommutative() && op1.similar(ae.op2) && op2.similar(ae.op1);
}
}
/**
* Does this expression use three operands?
*/
public boolean isTernary() {
return op3 != null;
}
/**
* Does this expression use two or more operands?
*/
public boolean isBinary() {
return op2 != null;
}
/**
* Does this expression represent the result of a load or store?
*/
public boolean isLoadOrStore() {
return GetField.conforms(opr) || GetStatic.conforms(opr) || PutField.conforms(opr) || PutStatic.conforms(opr);
}
/**
* Does this expression represent the result of a load?
*/
public boolean isLoad() {
return GetField.conforms(opr) || GetStatic.conforms(opr);
}
/**
* Does this expression represent the result of a store?
*/
public boolean isStore() {
return PutField.conforms(opr) || PutStatic.conforms(opr);
}
/**
* Does this expression represent the result of a bounds check?
*/
public boolean isBoundsCheck() {
return BoundsCheck.conforms(opr) || (TrapIf.conforms(opr) && ((OPT_TrapCodeOperand) op3).isArrayBounds());
}
/**
* Is this expression commutative?
*/
public boolean isCommutative() {
return opr.isCommutative();
}
}
}