/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* 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/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt.ssa;
import static org.jikesrvm.compilers.opt.ir.Operators.*;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.HashMap;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.DefUse;
import org.jikesrvm.compilers.opt.OptOptions;
import org.jikesrvm.compilers.opt.Simple;
import org.jikesrvm.compilers.opt.controlflow.DominatorTree;
import org.jikesrvm.compilers.opt.controlflow.DominatorTreeNode;
import org.jikesrvm.compilers.opt.driver.CompilerPhase;
import org.jikesrvm.compilers.opt.ir.BBend;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.GuardResultCarrier;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.ResultCarrier;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.util.TreeNode;
/**
* This class provides global common sub expression elimination.
*/
public final class GlobalCSE extends CompilerPhase {
/** Output debug messages */
public boolean verbose = true;
/** Cache of IR being processed by this phase */
private IR ir;
/** Cache of the value numbers from the IR */
private GlobalValueNumberState valueNumbers;
/**
* Cache of dominator tree that should be computed prior to this
* phase
*/
private DominatorTree dominator;
/**
* Available expressions. From Muchnick, "an expression
* <em>exp</em>is said to be <em>available</em> at the entry to a
* basic block if along every control-flow path from the entry block
* to this block there is an evaluation of exp that is not
* subsequently killed by having one or more of its operands
* assigned a new value." Our available expressions are a mapping
* from a value number to the first instruction to define it as we
* traverse the dominator tree.
*/
private final HashMap<Integer, Instruction> avail;
/**
* Constructor
*/
public GlobalCSE() {
avail = new HashMap<Integer, Instruction>();
}
/**
* Redefine shouldPerform so that none of the subphases will occur
* unless we pass through this test.
*/
@Override
public boolean shouldPerform(OptOptions options) {
return options.SSA_GCSE;
}
/**
* Constructor for this compiler phase
*/
private static final Constructor<CompilerPhase> constructor = getCompilerPhaseConstructor(GlobalCSE.class);
/**
* Get a constructor object for this compiler phase
* @return compiler phase constructor
*/
@Override
public Constructor<CompilerPhase> getClassConstructor() {
return constructor;
}
/**
* Returns the name of the phase
*/
@Override
public String getName() {
return "Global CSE";
}
/**
* Perform the GlobalCSE compiler phase
*/
@Override
public void perform(IR ir) {
// conditions to leave early
if (ir.hasReachableExceptionHandlers() || GCP.tooBig(ir)) {
return;
}
// cache useful values
verbose = ir.options.DEBUG_GCP;
this.ir = ir;
dominator = ir.HIRInfo.dominatorTree;
// perform GVN
(new GlobalValueNumber()).perform(ir);
valueNumbers = ir.HIRInfo.valueNumbers;
if (verbose) VM.sysWriteln("in GCSE for " + ir.method);
// compute DU and perform copy propagation
DefUse.computeDU(ir);
Simple.copyPropagation(ir);
DefUse.computeDU(ir);
// perform GCSE starting at the entry block
globalCSE(ir.firstBasicBlockInCodeOrder());
if (VM.VerifyAssertions) {
boolean isEmpty = avail.isEmpty();
if (!isEmpty) {
String msg = avail.toString();
VM._assert(isEmpty, msg);
}
}
ir.actualSSAOptions.setScalarValid(false);
}
/**
* Recursively descend over all blocks dominated by b. For each
* instruction in the block, if it defines a GVN then record it in
* the available expressions. If the GVN already exists in the
* available expressions then eliminate the instruction and change
* all uses of the result of the instruction to be uses of the first
* instruction to define the result of this expression.
* @param b the current block to process
*/
private void globalCSE(BasicBlock b) {
Instruction next, inst;
// Iterate over instructions in b
inst = b.firstInstruction();
while (!BBend.conforms(inst)) {
next = inst.nextInstructionInCodeOrder();
// check instruction is safe for GCSE, {@see shouldCSE}
if (!shouldCSE(inst)) {
inst = next;
continue;
}
// check the instruction defines a result
RegisterOperand result = getResult(inst);
if (result == null) {
inst = next;
continue;
}
// get the value number for this result. The value number for
// the same sub-expression is shared by all results showing they
// can be eliminated. If the value number is UNKNOWN the result
// is negative.
int vn = valueNumbers.getValueNumber(result);
if (vn < 0) {
inst = next;
continue;
}
// was this the first definition of the value number?
Integer Vn = vn;
Instruction former = avail.get(Vn);
if (former == null) {
// first occurance of value number, record it in the available
// expressions
avail.put(Vn, inst);
} else {
// this value number has been seen before so we can use the
// earlier version
// NB instead of trying to repair Heap SSA, we rebuild it
// after CSE
// relink scalar dependencies - make all uses of the current
// instructions result use the first definition of the result
// by the earlier expression
RegisterOperand formerDef = getResult(former);
Register reg = result.getRegister();
formerDef.getRegister().setSpansBasicBlock();
Enumeration<RegisterOperand> uses = DefUse.uses(reg);
while (uses.hasMoreElements()) {
RegisterOperand use = uses.nextElement();
DefUse.transferUse(use, formerDef);
}
if (verbose) {
VM.sysWriteln("using " + former + "\n" + "instead of " + inst);
}
// remove the redundant instruction
inst.remove();
}
inst = next;
} // end of instruction iteration
// Recurse over all blocks that this block dominates
Enumeration<TreeNode> e = dominator.getChildren(b);
while (e.hasMoreElements()) {
DominatorTreeNode n = (DominatorTreeNode) e.nextElement();
BasicBlock bl = n.getBlock();
// don't process infrequently executed basic blocks
if (ir.options.FREQ_FOCUS_EFFORT && bl.getInfrequent()) continue;
globalCSE(bl);
}
// Iterate over instructions in this basic block removing
// available expressions that had been created for this block
inst = b.firstInstruction();
while (!BBend.conforms(inst)) {
next = inst.nextInstructionInCodeOrder();
if (!shouldCSE(inst)) {
inst = next;
continue;
}
RegisterOperand result = getResult(inst);
if (result == null) {
inst = next;
continue;
}
int vn = valueNumbers.getValueNumber(result);
if (vn < 0) {
inst = next;
continue;
}
Integer Vn = vn;
Instruction former = avail.get(Vn);
if (former == inst) {
avail.remove(Vn);
}
inst = next;
}
}
private RegisterOperand getResult(Instruction inst) {
if (ResultCarrier.conforms(inst)) {
return ResultCarrier.getResult(inst);
}
if (GuardResultCarrier.conforms(inst)) {
return GuardResultCarrier.getGuardResult(inst);
}
return null;
}
private boolean shouldCSE(Instruction inst) {
if ((inst.isAllocation()) ||
inst.isDynamicLinkingPoint() ||
inst.isImplicitLoad() ||
inst.isImplicitStore() ||
inst.getOpcode() >= ARCH_INDEPENDENT_END_opcode) {
return false;
}
switch (inst.getOpcode()) {
case INT_MOVE_opcode:
case LONG_MOVE_opcode:
case CHECKCAST_opcode:
case CHECKCAST_NOTNULL_opcode:
case CHECKCAST_UNRESOLVED_opcode:
case MUST_IMPLEMENT_INTERFACE_opcode:
case INSTANCEOF_opcode:
case INSTANCEOF_NOTNULL_opcode:
case INSTANCEOF_UNRESOLVED_opcode:
case PI_opcode:
case FLOAT_MOVE_opcode:
case DOUBLE_MOVE_opcode:
case REF_MOVE_opcode:
case GUARD_MOVE_opcode:
case GUARD_COMBINE_opcode:
case TRAP_IF_opcode:
case REF_ADD_opcode:
case INT_ADD_opcode:
case LONG_ADD_opcode:
case FLOAT_ADD_opcode:
case DOUBLE_ADD_opcode:
case REF_SUB_opcode:
case INT_SUB_opcode:
case LONG_SUB_opcode:
case FLOAT_SUB_opcode:
case DOUBLE_SUB_opcode:
case INT_MUL_opcode:
case LONG_MUL_opcode:
case FLOAT_MUL_opcode:
case DOUBLE_MUL_opcode:
case INT_DIV_opcode:
case LONG_DIV_opcode:
case FLOAT_DIV_opcode:
case DOUBLE_DIV_opcode:
case INT_REM_opcode:
case LONG_REM_opcode:
case FLOAT_REM_opcode:
case DOUBLE_REM_opcode:
case INT_NEG_opcode:
case LONG_NEG_opcode:
case FLOAT_NEG_opcode:
case DOUBLE_NEG_opcode:
case REF_SHL_opcode:
case INT_SHL_opcode:
case LONG_SHL_opcode:
case REF_SHR_opcode:
case INT_SHR_opcode:
case LONG_SHR_opcode:
case REF_USHR_opcode:
case INT_USHR_opcode:
case LONG_USHR_opcode:
case REF_AND_opcode:
case INT_AND_opcode:
case LONG_AND_opcode:
case REF_OR_opcode:
case INT_OR_opcode:
case LONG_OR_opcode:
case REF_XOR_opcode:
case INT_XOR_opcode:
case REF_NOT_opcode:
case INT_NOT_opcode:
case LONG_NOT_opcode:
case LONG_XOR_opcode:
case INT_2LONG_opcode:
case INT_2FLOAT_opcode:
case INT_2DOUBLE_opcode:
case INT_2ADDRSigExt_opcode:
case INT_2ADDRZerExt_opcode:
case LONG_2ADDR_opcode:
case ADDR_2INT_opcode:
case ADDR_2LONG_opcode:
case LONG_2INT_opcode:
case LONG_2FLOAT_opcode:
case LONG_2DOUBLE_opcode:
case FLOAT_2INT_opcode:
case FLOAT_2LONG_opcode:
case FLOAT_2DOUBLE_opcode:
case DOUBLE_2INT_opcode:
case DOUBLE_2LONG_opcode:
case DOUBLE_2FLOAT_opcode:
case INT_2BYTE_opcode:
case INT_2USHORT_opcode:
case INT_2SHORT_opcode:
case LONG_CMP_opcode:
case FLOAT_CMPL_opcode:
case FLOAT_CMPG_opcode:
case DOUBLE_CMPL_opcode:
case DOUBLE_CMPG_opcode:
case NULL_CHECK_opcode:
case BOUNDS_CHECK_opcode:
case INT_ZERO_CHECK_opcode:
case LONG_ZERO_CHECK_opcode:
case OBJARRAY_STORE_CHECK_opcode:
case OBJARRAY_STORE_CHECK_NOTNULL_opcode:
case BOOLEAN_NOT_opcode:
case BOOLEAN_CMP_INT_opcode:
case BOOLEAN_CMP_ADDR_opcode:
case FLOAT_AS_INT_BITS_opcode:
case INT_BITS_AS_FLOAT_opcode:
case DOUBLE_AS_LONG_BITS_opcode:
case LONG_BITS_AS_DOUBLE_opcode:
case ARRAYLENGTH_opcode:
case GET_OBJ_TIB_opcode:
case GET_CLASS_TIB_opcode:
case GET_TYPE_FROM_TIB_opcode:
case GET_SUPERCLASS_IDS_FROM_TIB_opcode:
case GET_DOES_IMPLEMENT_FROM_TIB_opcode:
case GET_ARRAY_ELEMENT_TIB_FROM_TIB_opcode:
return !(GCP.usesOrDefsPhysicalRegisterOrAddressType(inst));
}
return false;
}
}