/*
* 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.Enumeration;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
import org.jikesrvm.compilers.opt.ir.OPT_Instruction;
import org.jikesrvm.compilers.opt.ir.OPT_InstructionEnumeration;
import org.jikesrvm.compilers.opt.ir.OPT_Operand;
import org.jikesrvm.compilers.opt.ir.OPT_OperandEnumeration;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PHI;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperandEnumeration;
import org.vmmagic.pragma.NoInline;
/**
* This class computes du-lists and associated information.
*
* <P> Note: DU operands are stored on the USE lists, but not the DEF
* lists.
*/
public final class OPT_DefUse {
static final boolean DEBUG = false;
static final boolean TRACE_DU_ACTIONS = false;
static final boolean SUPRESS_DU_FOR_PHYSICALS = true;
/**
* Clear defList, useList for an IR.
*
* @param ir the IR in question
*/
public static void clearDU(OPT_IR ir) {
for (OPT_Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = reg.getNext()) {
reg.defList = null;
reg.useList = null;
reg.scratch = -1;
reg.clearSeenUse();
}
for (Enumeration<OPT_Register> e = ir.regpool.getPhysicalRegisterSet().enumerateAll(); e.hasMoreElements();) {
OPT_Register reg = e.nextElement();
reg.defList = null;
reg.useList = null;
reg.scratch = -1;
reg.clearSeenUse();
}
if (TRACE_DU_ACTIONS || DEBUG) {
VM.sysWrite("Cleared DU\n");
}
}
/**
* Compute the register list and def-use lists for a method.
*
* @param ir the IR in question
*/
@NoInline
public static void computeDU(OPT_IR ir) {
// Clear old register list (if any)
clearDU(ir);
// Create register defList and useList
for (OPT_Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr =
instr.nextInstructionInCodeOrder()) {
OPT_OperandEnumeration defs = instr.getPureDefs();
OPT_OperandEnumeration uses = instr.getUses();
while (defs.hasMoreElements()) {
OPT_Operand op = defs.next();
if (op instanceof OPT_RegisterOperand) {
OPT_RegisterOperand rop = (OPT_RegisterOperand) op;
recordDef(rop);
}
} // for ( defs = ... )
while (uses.hasMoreElements()) {
OPT_Operand op = uses.next();
if (op instanceof OPT_RegisterOperand) {
OPT_RegisterOperand rop = (OPT_RegisterOperand) op;
recordUse(rop);
}
} // for ( uses = ... )
} // for ( instr = ... )
// Remove any symbloic registers with no uses/defs from
// the register pool. We'll waste analysis time keeping them around.
OPT_Register next;
for (OPT_Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = next) {
next = reg.getNext();
if (reg.defList == null && reg.useList == null) {
if (DEBUG) {
VM.sysWrite("Removing " + reg + " from the register pool\n");
}
ir.regpool.removeRegister(reg);
}
}
}
/**
* Record a use of a register
* @param regOp the operand that uses the register
*/
static void recordUse(OPT_RegisterOperand regOp) {
OPT_Register reg = regOp.getRegister();
regOp.append(reg.useList);
reg.useList = regOp;
reg.useCount++;
}
/**
* Record a def/use of a register
* TODO: For now we just pretend this is a use!!!!
*
* @param regOp the operand that uses the register
*/
public static void recordDefUse(OPT_RegisterOperand regOp) {
OPT_Register reg = regOp.getRegister();
if (SUPRESS_DU_FOR_PHYSICALS && reg.isPhysical()) return;
regOp.append(reg.useList);
reg.useList = regOp;
}
/**
* Record a def of a register
* @param regOp the operand that uses the register
*/
static void recordDef(OPT_RegisterOperand regOp) {
OPT_Register reg = regOp.getRegister();
if (SUPRESS_DU_FOR_PHYSICALS && reg.isPhysical()) return;
regOp.append(reg.defList);
reg.defList = regOp;
}
/**
* Record that a use of a register no longer applies
* @param regOp the operand that uses the register
*/
public static void removeUse(OPT_RegisterOperand regOp) {
OPT_Register reg = regOp.getRegister();
if (SUPRESS_DU_FOR_PHYSICALS && reg.isPhysical()) return;
if (regOp == reg.useList) {
reg.useList = reg.useList.getNext();
} else {
OPT_RegisterOperand prev = reg.useList;
OPT_RegisterOperand curr = prev.getNext();
while (curr != regOp) {
prev = curr;
curr = curr.getNext();
}
prev.setNext(curr.getNext());
}
reg.useCount--;
if (DEBUG) {
VM.sysWrite("removed a use " + regOp.instruction + "\n");
printUses(reg);
}
}
/**
* Record that a def of a register no longer applies
* @param regOp the operand that uses the register
*/
public static void removeDef(OPT_RegisterOperand regOp) {
OPT_Register reg = regOp.getRegister();
if (SUPRESS_DU_FOR_PHYSICALS && reg.isPhysical()) return;
if (regOp == reg.defList) {
reg.defList = reg.defList.getNext();
} else {
OPT_RegisterOperand prev = reg.defList;
OPT_RegisterOperand curr = prev.getNext();
while (curr != regOp) {
prev = curr;
curr = curr.getNext();
}
prev.setNext(curr.getNext());
}
if (DEBUG) {
VM.sysWrite("removed a def " + regOp.instruction + "\n");
printDefs(reg);
}
}
/**
* This code changes the use in <code>origRegOp</code> to use
* the use in <code>newRegOp</code>.
*
* <p> If the type of <code>origRegOp</code> is not a reference, but the
* type of <code>newRegOp</code> is a reference, we need to update
* <code>origRegOp</code> to be a reference.
* Otherwise, the GC map code will be incorrect. -- Mike Hind
* @param origRegOp the register operand to change
* @param newRegOp the register operand to use for the change
*/
static void transferUse(OPT_RegisterOperand origRegOp, OPT_RegisterOperand newRegOp) {
if (VM.VerifyAssertions) {
VM._assert(origRegOp.getRegister().getType() == newRegOp.getRegister().getType());
}
OPT_Instruction inst = origRegOp.instruction;
if (DEBUG) {
VM.sysWrite("Transfering a use of " + origRegOp + " in " + inst + " to " + newRegOp + "\n");
}
removeUse(origRegOp);
// check to see if the regOp type is NOT a ref, but the newRegOp type
// is a reference. This can occur because of magic calls.
if (!origRegOp.getType().isReferenceType() && newRegOp.getType().isReferenceType()) {
// clone the newRegOp object and use it to replace the regOp object
OPT_RegisterOperand copiedRegOp = (OPT_RegisterOperand) newRegOp.copy();
inst.replaceOperand(origRegOp, copiedRegOp);
recordUse(copiedRegOp);
} else {
// just copy the register
origRegOp.setRegister(newRegOp.getRegister());
recordUse(origRegOp);
}
if (DEBUG) {
printUses(origRegOp.getRegister());
printUses(newRegOp.getRegister());
}
}
/**
* Remove an instruction and update register lists.
*/
static void removeInstructionAndUpdateDU(OPT_Instruction s) {
for (OPT_OperandEnumeration e = s.getPureDefs(); e.hasMoreElements();) {
OPT_Operand op = e.next();
if (op instanceof OPT_RegisterOperand) {
removeDef((OPT_RegisterOperand) op);
}
}
for (OPT_OperandEnumeration e = s.getUses(); e.hasMoreElements();) {
OPT_Operand op = e.next();
if (op instanceof OPT_RegisterOperand) {
removeUse((OPT_RegisterOperand) op);
}
}
s.remove();
}
/**
* Update register lists to account for the effect of a new
* instruction s
*/
public static void updateDUForNewInstruction(OPT_Instruction s) {
for (OPT_OperandEnumeration e = s.getPureDefs(); e.hasMoreElements();) {
OPT_Operand op = e.next();
if (op instanceof OPT_RegisterOperand) {
recordDef((OPT_RegisterOperand) op);
}
}
for (OPT_OperandEnumeration e = s.getUses(); e.hasMoreElements();) {
OPT_Operand op = e.next();
if (op instanceof OPT_RegisterOperand) {
recordUse((OPT_RegisterOperand) op);
}
}
}
/**
* Replace an instruction and update register lists.
*/
static void replaceInstructionAndUpdateDU(OPT_Instruction oldI, OPT_Instruction newI) {
oldI.insertBefore(newI);
removeInstructionAndUpdateDU(oldI);
updateDUForNewInstruction(newI);
}
/**
* Enumerate all operands that use a given register.
*/
static OPT_RegisterOperandEnumeration uses(OPT_Register reg) {
return new RegOpListWalker(reg.useList);
}
/**
* Enumerate all operands that def a given register.
*/
public static OPT_RegisterOperandEnumeration defs(OPT_Register reg) {
return new RegOpListWalker(reg.defList);
}
/**
* Does a given register have exactly one use?
*/
static boolean exactlyOneUse(OPT_Register reg) {
return (reg.useList != null) && (reg.useList.getNext() == null);
}
/**
* Print all the instructions that def a register.
* @param reg
*/
static void printDefs(OPT_Register reg) {
VM.sysWrite("Definitions of " + reg + '\n');
for (OPT_RegisterOperandEnumeration e = defs(reg); e.hasMoreElements();) {
VM.sysWrite("\t" + e.next().instruction + "\n");
}
}
/**
* Print all the instructions that usea register.
* @param reg
*/
static void printUses(OPT_Register reg) {
VM.sysWrite("Uses of " + reg + '\n');
for (OPT_RegisterOperandEnumeration e = uses(reg); e.hasMoreElements();) {
VM.sysWrite("\t" + e.next().instruction + "\n");
}
}
/**
* Recompute <code> isSSA </code> for all registers by traversing register
* list.
* NOTE: the DU MUST be computed BEFORE calling this function
*
* @param ir the IR in question
*/
public static void recomputeSSA(OPT_IR ir) {
// Use register /ist to enumerate register objects (FAST)
for (OPT_Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = reg.getNext()) {
// Set isSSA = true iff reg has exactly one static definition.
reg.putSSA((reg.defList != null && reg.defList.getNext() == null));
}
}
/**
* Merge register reg2 into register reg1.
* Remove reg2 from the DU information
*/
public static void mergeRegisters(OPT_IR ir, OPT_Register reg1, OPT_Register reg2) {
OPT_RegisterOperand lastOperand;
if (reg1 == reg2) {
return;
}
if (DEBUG) {
VM.sysWrite("Merging " + reg2 + " into " + reg1 + "\n");
printDefs(reg2);
printUses(reg2);
printDefs(reg1);
printUses(reg1);
}
// first loop through defs of reg2 (currently, there will only be one def)
lastOperand = null;
for (OPT_RegisterOperand def = reg2.defList; def != null; lastOperand = def, def = def.getNext()) {
// Change def to refer to reg1 instead
def.setRegister(reg1);
// Track lastOperand
lastOperand = def;
}
if (lastOperand != null) {
// Set reg1.defList = concat(reg2.defList, reg1.deflist)
lastOperand.setNext(reg1.defList);
reg1.defList = reg2.defList;
}
// now loop through uses
lastOperand = null;
for (OPT_RegisterOperand use = reg2.useList; use != null; use = use.getNext()) {
// Change use to refer to reg1 instead
use.setRegister(reg1);
// Track lastOperand
lastOperand = use;
}
if (lastOperand != null) {
// Set reg1.useList = concat(reg2.useList, reg1.uselist)
lastOperand.setNext(reg1.useList);
reg1.useList = reg2.useList;
}
// Remove reg2 from RegisterPool
ir.regpool.removeRegister(reg2);
if (DEBUG) {
VM.sysWrite("Merge complete\n");
printDefs(reg1);
printUses(reg1);
}
}
/**
* Recompute spansBasicBlock flags for all registers.
*
* @param ir the IR in question
*/
public static void recomputeSpansBasicBlock(OPT_IR ir) {
// clear fields
for (OPT_Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = reg.getNext()) {
reg.scratch = -1;
reg.clearSpansBasicBlock();
}
// iterate over the basic blocks
for (OPT_BasicBlock bb = ir.firstBasicBlockInCodeOrder(); bb != null; bb = bb.nextBasicBlockInCodeOrder()) {
int bbNum = bb.getNumber();
// enumerate the instructions in the basic block
for (OPT_InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
OPT_Instruction inst = e.next();
// check each Operand in the instruction
for (OPT_OperandEnumeration ops = inst.getOperands(); ops.hasMoreElements();) {
OPT_Operand op = ops.next();
if (op instanceof OPT_RegisterOperand) {
OPT_Register reg = ((OPT_RegisterOperand) op).getRegister();
if (reg.isPhysical()) {
continue;
}
if (reg.spansBasicBlock()) {
continue;
}
if (seenInDifferentBlock(reg, bbNum)) {
reg.setSpansBasicBlock();
continue;
}
if (inst.operator() == PHI) {
reg.setSpansBasicBlock();
continue;
}
logAppearance(reg, bbNum);
}
}
}
}
}
/**
* Mark that we have seen a register in a particular
* basic block, and whether we saw a use
*
* @param reg the register
* @param bbNum the number of the basic block
*/
private static void logAppearance(OPT_Register reg, int bbNum) {
reg.scratch = bbNum;
}
/**
* Have we seen this register in a different basic block?
*
* @param reg the register
* @param bbNum the number of the basic block
*/
private static boolean seenInDifferentBlock(OPT_Register reg, int bbNum) {
int bb = reg.scratch;
return (bb != -1) && (bb != bbNum);
}
/**
* Utility class to encapsulate walking a use/def list.
*/
private static final class RegOpListWalker implements OPT_RegisterOperandEnumeration {
private OPT_RegisterOperand current;
RegOpListWalker(OPT_RegisterOperand start) {
current = start;
}
public boolean hasMoreElements() {
return current != null;
}
public OPT_RegisterOperand nextElement() {
return next();
}
public OPT_RegisterOperand next() {
if (current == null) raiseNoSuchElementException();
OPT_RegisterOperand tmp = current;
current = current.getNext();
return tmp;
}
@NoInline
private static void raiseNoSuchElementException() {
throw new java.util.NoSuchElementException("RegOpListWalker");
}
}
}