/*
* 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.ia32;
import org.jikesrvm.compilers.opt.OPT_CompilerPhase;
import org.jikesrvm.compilers.opt.ir.MIR_LowTableSwitch;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlockEnumeration;
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 org.jikesrvm.compilers.opt.ir.OPT_Operators;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
/**
* This class splits live ranges for certain special cases to ensure
* correctness during IA32 register allocation.
*/
class OPT_MIRSplitRanges extends OPT_CompilerPhase implements OPT_Operators {
/**
* 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;
}
/**
* Return the name of this phase
* @return "Live Range Splitting"
*/
public final String getName() {
return "MIR Range Splitting";
}
/**
* The main method.
*
* We split live ranges for registers around PEIs which have catch
* blocks. Suppose we have a
* PEI s which uses a symbolic register r1. We must ensure that after
* register allocation, r1 is NOT assigned to a scratch location in s,
* since this would mess up code in the catch block tghat uses r1.
*
* So, instead, we introduce a new temporary r2 which holds the value of
* r1. The live range for r2 spans only the instruction s. Later, we
* will ensure that r2 is never spilled.
*
* TODO: This could be implemented more efficiently.
*
* @param ir the governing IR
*/
public final void perform(OPT_IR ir) {
java.util.HashMap<OPT_Register, OPT_Register> newMap = new java.util.HashMap<OPT_Register, OPT_Register>(5);
for (OPT_BasicBlockEnumeration be = ir.getBasicBlocks(); be.hasMoreElements();) {
OPT_BasicBlock bb = be.nextElement();
for (OPT_InstructionEnumeration ie = bb.forwardInstrEnumerator(); ie.hasMoreElements();) {
OPT_Instruction s = ie.next();
// clear the cache of register assignments
newMap.clear();
// Split live ranges at PEIs and a few special cases to
// make sure we can pin values that must be in registers.
// NOTE: Any operator that is an IA32 special case that must have
// a particular operand in a register must be mentioned both
// here and in OPT_RegisterRestrictions!
if (s.isPEI() && s.operator != IR_PROLOGUE) {
if (bb.hasApplicableExceptionalOut(s) || !OPT_RegisterRestrictions.SCRATCH_IN_PEI) {
splitAllLiveRanges(s, newMap, ir, false);
}
}
// handle special cases for IA32
// (1) Some operands must be in registers
switch (s.getOpcode()) {
case MIR_LOWTABLESWITCH_opcode: {
OPT_RegisterOperand rOp = MIR_LowTableSwitch.getIndex(s);
OPT_RegisterOperand temp = findOrCreateTemp(rOp, newMap, ir);
// NOTE: Index as marked as a DU because LowTableSwitch is
// going to destroy the value in the register.
// By construction (see ConvertToLowLevelIR), no one will
// every read the value computed by a LowTableSwitch.
// Therefore, don't insert a move instruction after the
// LowTableSwitch (which would cause IR verification
// problems anyways, since LowTableSwitch is a branch).
insertMoveBefore(temp, rOp.copyRO(), s); // move r into 'temp' before s
rOp.setRegister(temp.getRegister());
}
break;
}
}
}
}
/**
* Split the live ranges of all register operands of an instruction
* @param s the instruction to process
* @param newMap a mapping from symbolics to temporaries
* @param ir the containing IR
* @param rootOnly only consider root operands?
*/
private static void splitAllLiveRanges(OPT_Instruction s, java.util.HashMap<OPT_Register, OPT_Register> newMap,
OPT_IR ir, boolean rootOnly) {
// walk over each USE
for (OPT_OperandEnumeration u = rootOnly ? s.getRootUses() : s.getUses(); u.hasMoreElements();) {
OPT_Operand use = u.next();
if (use.isRegister()) {
OPT_RegisterOperand rUse = use.asRegister();
OPT_RegisterOperand temp = findOrCreateTemp(rUse, newMap, ir);
// move 'use' into 'temp' before s
insertMoveBefore(temp, rUse.copyRO(), s);
}
}
// walk over each DEF (by defintion defs == root defs)
for (OPT_OperandEnumeration d = s.getDefs(); d.hasMoreElements();) {
OPT_Operand def = d.next();
if (def.isRegister()) {
OPT_RegisterOperand rDef = def.asRegister();
OPT_RegisterOperand temp = findOrCreateTemp(rDef, newMap, ir);
// move 'temp' into 'r' after s
insertMoveAfter(rDef.copyRO(), temp, s);
}
}
// Now go back and replace the registers.
for (OPT_OperandEnumeration ops = rootOnly ? s.getRootOperands() : s.getOperands(); ops.hasMoreElements();) {
OPT_Operand op = ops.next();
if (op.isRegister()) {
OPT_RegisterOperand rOp = op.asRegister();
OPT_Register r = rOp.getRegister();
OPT_Register newR = newMap.get(r);
if (newR != null) {
rOp.setRegister(newR);
}
}
}
}
/**
* Find or create a temporary register to cache a symbolic register.
*
* @param rOp the symbolic register
* @param map a mapping from symbolics to temporaries
* @param ir the governing IR
*/
private static OPT_RegisterOperand findOrCreateTemp(OPT_RegisterOperand rOp,
java.util.HashMap<OPT_Register, OPT_Register> map, OPT_IR ir) {
OPT_Register tReg = map.get(rOp.getRegister());
if (tReg == null) {
OPT_RegisterOperand tOp = ir.regpool.makeTemp(rOp.getType());
map.put(rOp.getRegister(), tOp.getRegister());
return tOp;
} else {
return new OPT_RegisterOperand(tReg, rOp.getType());
}
}
/**
* Insert an instruction to move r1 into r2 before instruction s
*/
private static void insertMoveBefore(OPT_RegisterOperand r2, OPT_RegisterOperand r1, OPT_Instruction s) {
OPT_Instruction m = OPT_PhysicalRegisterTools.makeMoveInstruction(r2, r1);
s.insertBefore(m);
}
/**
* Insert an instruction to move r1 into r2 after instruction s
*/
private static void insertMoveAfter(OPT_RegisterOperand r2, OPT_RegisterOperand r1, OPT_Instruction s) {
OPT_Instruction m = OPT_PhysicalRegisterTools.makeMoveInstruction(r2, r1);
s.insertAfter(m);
}
}