/*
* 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 java.util.HashMap;
import java.util.HashSet;
import org.jikesrvm.classloader.VM_TypeReference;
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 static org.jikesrvm.compilers.opt.ir.OPT_Operators.SPLIT;
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.jikesrvm.compilers.opt.ir.Unary;
/**
* Perform live-range splitting.
*
* <p>This pass splits live ranges where they enter and exit loop bodies
* by normal (unexceptional) control flow.
* It splits a live range for register r by inserting the instruction
* <code> r = SPLIT r </code>. Then, SSA renaming will introduce a new
* name for r. The SPLIT operator is later turned into a MOVE during
* BURS.
*
* <p>This pass also splits live ranges on edges to and from infrequent code.
*
* <p> This composite phase should be performed at the end of SSA in LIR.
*/
class OPT_LiveRangeSplitting extends OPT_OptimizationPlanCompositeElement {
public final boolean shouldPerform(OPT_Options options) {
return options.LIVE_RANGE_SPLITTING;
}
/**
* Build this phase as a composite of others.
*/
OPT_LiveRangeSplitting() {
super("LIR SSA Live Range Splitting", new OPT_OptimizationPlanElement[]{
// 0. Clean up the IR
new OPT_OptimizationPlanAtomicElement(new OPT_BranchOptimizations(2, true, true)),
new OPT_OptimizationPlanAtomicElement(new OPT_CoalesceMoves()),
// 1. Insert the split operations.
new OPT_OptimizationPlanAtomicElement(new LiveRangeSplitting()),
new OPT_OptimizationPlanAtomicElement(new OPT_BranchOptimizations(2, true, true)),
// 2. Use SSA to rename
new OPT_OptimizationPlanAtomicElement(new OPT_DominatorsPhase(true)),
new OPT_OptimizationPlanAtomicElement(new OPT_DominanceFrontier()),
new OPT_OptimizationPlanAtomicElement(new RenamePreparation()),
new OPT_OptimizationPlanAtomicElement(new OPT_EnterSSA()),
new OPT_OptimizationPlanAtomicElement(new OPT_LeaveSSA())});
}
private static class LiveRangeSplitting extends OPT_CompilerPhase {
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public OPT_CompilerPhase newExecution(OPT_IR ir) {
return this;
}
public final boolean shouldPerform(OPT_Options options) {
return options.LIVE_RANGE_SPLITTING;
}
public final String getName() {
return "Live Range Splitting";
}
/**
* The main entrypoint for this pass.
*/
public final void perform(OPT_IR ir) {
// 1. Compute an up-to-date loop structure tree.
OPT_DominatorsPhase dom = new OPT_DominatorsPhase(true);
dom.perform(ir);
OPT_LSTGraph lst = ir.HIRInfo.LoopStructureTree;
if (lst == null) {
throw new OPT_OptimizingCompilerException("null loop structure tree");
}
// 2. Compute liveness.
// YUCK: We will later retrieve the live analysis info, relying on the
// scratchObject field of the Basic Blocks. Thus, liveness must be
// computed AFTER the dominators, since the dominator phase also uses
// the scratchObject field.
OPT_LiveAnalysis live = new OPT_LiveAnalysis(false, // don't create GC maps
false, // don't skip (final) local
// propagation step of live analysis
false, // don't store information at handlers
true); // skip guards
live.perform(ir);
// 3. Perform the analysis
OPT_DefUse.computeDU(ir);
HashMap<BasicBlockPair, HashSet<OPT_Register>> result = findSplitPoints(ir, live, lst);
// 4. Perform the transformation.
transform(ir, result);
// 5. Record that we've destroyed SSA
if (ir.actualSSAOptions != null) {
ir.actualSSAOptions.setScalarValid(false);
ir.actualSSAOptions.setHeapValid(false);
}
}
/**
* Find the points the IR where live ranges should be split.
*
* @param ir the governing IR
* @param live valid liveness information
* @param lst a valid loop structure tree
* @return the result as a mapping from BasicBlockPair to a set of registers
*/
private static HashMap<BasicBlockPair, HashSet<OPT_Register>> findSplitPoints(OPT_IR ir, OPT_LiveAnalysis live,
OPT_LSTGraph lst) {
HashMap<BasicBlockPair, HashSet<OPT_Register>> result = new HashMap<BasicBlockPair, HashSet<OPT_Register>>(10);
for (Enumeration<OPT_GraphNode> e = lst.enumerateNodes(); e.hasMoreElements();) {
OPT_LSTNode node = (OPT_LSTNode) e.nextElement();
OPT_BasicBlock header = node.getHeader();
OPT_BitVector loop = node.getLoop();
if (loop == null) continue;
// First split live ranges on edges coming into the loop header.
for (Enumeration<OPT_BasicBlock> in = header.getIn(); in.hasMoreElements();) {
OPT_BasicBlock bb = in.nextElement();
if (loop.get(bb.getNumber())) continue;
HashSet<OPT_Register> liveRegisters = live.getLiveRegistersOnEdge(bb, header);
for (OPT_Register r : liveRegisters) {
if (r.isSymbolic()) {
HashSet<OPT_Register> s = findOrCreateSplitSet(result, bb, header);
s.add(r);
}
}
}
// Next split live ranges on every normal exit from the loop.
for (int i = 0; i < loop.length(); i++) {
if (loop.get(i)) {
OPT_BasicBlock bb = ir.getBasicBlock(i);
for (Enumeration<OPT_BasicBlock> out = bb.getNormalOut(); out.hasMoreElements();) {
OPT_BasicBlock dest = out.nextElement();
if (loop.get(dest.getNumber())) continue;
HashSet<OPT_Register> liveRegisters = live.getLiveRegistersOnEdge(bb, dest);
for (OPT_Register r : liveRegisters) {
if (r.isSymbolic()) {
HashSet<OPT_Register> s = findOrCreateSplitSet(result, bb, dest);
s.add(r);
}
}
}
}
}
}
addEntriesForInfrequentBlocks(ir, live, result);
return result;
}
/**
* Split live ranges on entry and exit to infrequent regions.
* Add this information to 'result', a mapping from BasicBlockPair to a set of
* registers to split.
*
* @param ir the governing IR
* @param live valid liveness information
* @param result mapping from BasicBlockPair to a set of registers
*/
private static void addEntriesForInfrequentBlocks(OPT_IR ir, OPT_LiveAnalysis live,
HashMap<BasicBlockPair, HashSet<OPT_Register>> result) {
for (Enumeration<OPT_BasicBlock> e = ir.getBasicBlocks(); e.hasMoreElements();) {
OPT_BasicBlock bb = e.nextElement();
boolean bbInfrequent = bb.getInfrequent();
for (Enumeration<OPT_BasicBlock> out = bb.getNormalOut(); out.hasMoreElements();) {
OPT_BasicBlock dest = out.nextElement();
boolean destInfrequent = dest.getInfrequent();
if (bbInfrequent ^ destInfrequent) {
HashSet<OPT_Register> liveRegisters = live.getLiveRegistersOnEdge(bb, dest);
for (OPT_Register r : liveRegisters) {
if (r.isSymbolic()) {
HashSet<OPT_Register> s = findOrCreateSplitSet(result, bb, dest);
s.add(r);
}
}
}
}
}
}
/**
* Given a mapping from BasicBlockPair -> HashSet, find or create the hash
* set corresponding to a given basic block pair
*
* @param map the mapping to search
* @param b1 the first basic block in the pair
* @param b2 the second basic block in the pair
*/
private static HashSet<OPT_Register> findOrCreateSplitSet(HashMap<BasicBlockPair, HashSet<OPT_Register>> map,
OPT_BasicBlock b1, OPT_BasicBlock b2) {
BasicBlockPair pair = new BasicBlockPair(b1, b2);
HashSet<OPT_Register> set = map.get(pair);
if (set == null) {
set = new HashSet<OPT_Register>(5);
map.put(pair, set);
}
return set;
}
/**
* Perform the transformation
*
* @param ir the governing IR
* @param xform a mapping from BasicBlockPair to the set of registers
* to split
*/
private static void transform(OPT_IR ir, HashMap<BasicBlockPair, HashSet<OPT_Register>> xform) {
for (BasicBlockPair bbp : xform.keySet()) {
HashSet<OPT_Register> toSplit = xform.get(bbp);
// we go ahead and split all edges, instead of just critical ones.
// we'll clean up the mess later after SSA.
OPT_BasicBlock target = OPT_IRTools.makeBlockOnEdge(bbp.src, bbp.dest, ir);
OPT_SSA.replaceBlockInPhis(bbp.dest, bbp.src, target);
for (OPT_Register r : toSplit) {
if (r.defList == null) continue;
OPT_Instruction s = null;
switch (r.getType()) {
case OPT_Register.ADDRESS_TYPE:
OPT_RegisterOperand lhs2 = OPT_IRTools.A(r);
OPT_RegisterOperand rhs2 = OPT_IRTools.A(r);
s = Unary.create(SPLIT, lhs2, rhs2);
// fix up types: only split live ranges when the type is
// consistent at all defs
VM_TypeReference t2 = null;
OPT_RegisterOperandEnumeration e2 = OPT_DefUse.defs(r);
if (!e2.hasMoreElements()) {
s = null;
} else {
OPT_RegisterOperand rop2 = e2.next();
t2 = rop2.getType();
while (e2.hasMoreElements()) {
OPT_RegisterOperand nextOp2 = e2.next();
if (nextOp2.getType() != t2) {
s = null;
}
}
}
if (s != null) {
lhs2.setType(t2);
rhs2.setType(t2);
}
break;
case OPT_Register.INTEGER_TYPE:
OPT_RegisterOperand lhs = OPT_IRTools.I(r);
OPT_RegisterOperand rhs = OPT_IRTools.I(r);
s = Unary.create(SPLIT, lhs, rhs);
// fix up types: only split live ranges when the type is
// consistent at all defs
VM_TypeReference t = null;
OPT_RegisterOperandEnumeration e = OPT_DefUse.defs(r);
if (!e.hasMoreElements()) {
s = null;
} else {
OPT_RegisterOperand rop = e.next();
t = rop.getType();
while (e.hasMoreElements()) {
OPT_RegisterOperand nextOp = e.next();
if (nextOp.getType() != t) {
s = null;
}
}
}
if (s != null) {
lhs.setType(t);
rhs.setType(t);
}
break;
case OPT_Register.FLOAT_TYPE:
s = Unary.create(SPLIT, OPT_IRTools.F(r), OPT_IRTools.F(r));
break;
case OPT_Register.DOUBLE_TYPE:
s = Unary.create(SPLIT, OPT_IRTools.D(r), OPT_IRTools.D(r));
break;
case OPT_Register.LONG_TYPE:
s = Unary.create(SPLIT, OPT_IRTools.L(r), OPT_IRTools.L(r));
break;
default:
// we won't split live ranges for other types.
s = null;
break;
}
if (s != null) {
target.prependInstruction(s);
}
}
}
}
/**
* A utility class to represent an edge in the CFG.
*/
private static class BasicBlockPair {
/**
* The source of a control-flow edge
*/
final OPT_BasicBlock src;
/**
* The sink of a control-flow edge
*/
final OPT_BasicBlock dest;
BasicBlockPair(OPT_BasicBlock src, OPT_BasicBlock dest) {
this.src = src;
this.dest = dest;
}
static int nextHash = 0;
int myHash = ++nextHash;
public int hashCode() {
return myHash;
}
public boolean equals(Object o) {
if (!(o instanceof BasicBlockPair)) return false;
BasicBlockPair p = (BasicBlockPair) o;
return (src.equals(p.src) && dest.equals(p.dest));
}
public String toString() {
return "<" + src + "," + dest + ">";
}
}
}
/**
* This class sets up the IR state prior to entering SSA.
*/
private static class RenamePreparation extends OPT_CompilerPhase {
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public OPT_CompilerPhase newExecution(OPT_IR ir) {
return this;
}
public final boolean shouldPerform(OPT_Options options) {
return options.LIVE_RANGE_SPLITTING;
}
public final String getName() {
return "Rename Preparation";
}
/**
* register in the IR the SSA properties we need for simple scalar
* renaming
*/
public final void perform(OPT_IR ir) {
ir.desiredSSAOptions = new OPT_SSAOptions();
ir.desiredSSAOptions.setScalarsOnly(true);
ir.desiredSSAOptions.setBackwards(false);
ir.desiredSSAOptions.setInsertUsePhis(false);
}
}
}