/*
* 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.ir;
import java.util.Enumeration;
import org.jikesrvm.ArchitectureSpecific.OPT_RegisterPool;
import org.jikesrvm.VM_Configuration;
import org.jikesrvm.classloader.VM_FieldReference;
import org.jikesrvm.classloader.VM_TypeReference;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BYTE_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BYTE_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.DOUBLE_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.DOUBLE_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.DOUBLE_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.DOUBLE_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.FLOAT_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.FLOAT_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.FLOAT_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.FLOAT_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GOTO;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GUARD_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.INT_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LONG_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_MOVE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.REF_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.SHORT_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.SHORT_STORE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.UBYTE_LOAD;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.USHORT_LOAD;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* This abstract class contains a bunch of useful static methods for
* performing operations on IR.
*/
public abstract class OPT_IRTools {
/**
* Create an integer register operand for a given register.
* To be used in passthrough expressions like
* <pre>
* ... Load.create(INT_LOAD, I(r2), A(r1), IC(4)) ...
* </pre>
*
* @param reg the given register
* @return integer register operand
*/
public static OPT_RegisterOperand A(OPT_Register reg) {
return new OPT_RegisterOperand(reg, VM_TypeReference.Address);
}
/**
* Create an integer register operand for a given register.
* To be used in passthrough expressions like
* <pre>
* ... Load.create(INT_LOAD, I(r2), A(r1), IC(4)) ...
* </pre>
* @param reg the given register
* @return integer register operand
*/
public static OPT_RegisterOperand I(OPT_Register reg) {
return new OPT_RegisterOperand(reg, VM_TypeReference.Int);
}
/**
* Create a float register operand for a given register.
* To be used in passthrough expressions like
* <pre>
* ... Load.create(FLOAT_LOAD, F(r2), A(r1), IC(4)) ...
* </pre>
*
* @param reg the given register
* @return float register operand
*/
public static OPT_RegisterOperand F(OPT_Register reg) {
return new OPT_RegisterOperand(reg, VM_TypeReference.Float);
}
/**
* Create a double register operand for a given register.
* To be used in passthrough expressions like
* <pre>
* ... Load.create(DOUBLE_LOAD, D(r2), A(r1), IC(4)) ...
* </pre>
*
* @param reg the given register
* @return double register operand
*/
public static OPT_RegisterOperand D(OPT_Register reg) {
return new OPT_RegisterOperand(reg, VM_TypeReference.Double);
}
/**
* Create a long register operand for a given register.
* To be used in passthrough expressions like
* <pre>
* ... Binary.create(LONG_LOAD, L(r2), A(r1), IC(4)) ...
* </pre>
*
* @param reg the given register
* @return long register operand
*/
public static OPT_RegisterOperand L(OPT_Register reg) {
return new OPT_RegisterOperand(reg, VM_TypeReference.Long);
}
/**
* Create a condition register operand for a given register.
* To be used in passthrough expressions like
* <pre>
* ... Binary.create(INT_CMP, CR(c2), I(r1), IC(4)) ...
* </pre>
*
* @param reg the given register
* @return condition register operand
*/
public static OPT_RegisterOperand CR(OPT_Register reg) {
return new OPT_RegisterOperand(reg, VM_TypeReference.Int);
}
/**
* Create an address constant operand with a given value.
* To be used in passthrough expressions like
* <pre>
* ...<op>.create(...., AC(Address.zero()) ...
* </pre>
*
* @param value The address constant
* @return address constant operand
*/
public static OPT_AddressConstantOperand AC(Address value) {
return new OPT_AddressConstantOperand(value);
}
public static OPT_AddressConstantOperand AC(Offset value) {
return new OPT_AddressConstantOperand(value);
}
/**
* Create an integer constant operand with a given value.
* To be used in passthrough expressions like
* <pre>
* ...<op>.create(...., IC(0) ...
* </pre>
*
* @param value The int constant
* @return integer constant operand
*/
public static OPT_IntConstantOperand IC(int value) {
return new OPT_IntConstantOperand(value);
}
/**
* Create a long constant operand with a given value.
* To be used in passthrough expressions like
* <pre>
* ...<op>.create(...., LC(0L) ...
* </pre>
*
* @param value the long value
* @return long constant operand
*/
public static OPT_LongConstantOperand LC(long value) {
return new OPT_LongConstantOperand(value);
}
/**
* Create a long constant operand with a given value.
* To be used in passthrough expressions like
* <pre>
* ...<op>.create(...., FC(0L) ...
* </pre>
*
* @param value the float value
* @return float constant operand
*/
public static OPT_FloatConstantOperand FC(float value) {
return new OPT_FloatConstantOperand(value);
}
/**
* Create a long constant operand with a given value.
* To be used in passthrough expressions like
* <pre>
* ...<op>.create(...., DC(0L) ...
* </pre>
*
* @param value the double value
* @return double constant operand
*/
public static OPT_DoubleConstantOperand DC(double value) {
return new OPT_DoubleConstantOperand(value);
}
/**
* Create a new OPT_TrueGuardOperand.
* To be used in passthrough expressions like
* <pre>
* ...<op>.create(...., TG() ...
* </pre>
*
* @return true guard operand
*/
public static OPT_TrueGuardOperand TG() {
return new OPT_TrueGuardOperand();
}
/**
* Copy the position information from the source instruction to
* the destination instruction, returning the source instruction.
* To be used in passthrough expressions like
* <pre>
* instr.insertBack(CPOS(instr, Load.create(...)));
* </pre>
*
* @param src the instruction to copy position information from
* @param dst the instruction to copy position information to
* @return dest
*/
public static OPT_Instruction CPOS(OPT_Instruction src, OPT_Instruction dst) {
dst.copyPosition(src);
return dst;
}
/**
* Returns a constant operand with a default value for a given type
*
* @param type desired type
* @return a constant operand with the default value for type
*/
public static OPT_Operand getDefaultOperand(VM_TypeReference type) {
if (type.isBooleanType()) return new OPT_IntConstantOperand(0);
if (type.isByteType()) return new OPT_IntConstantOperand(0);
if (type.isCharType()) return new OPT_IntConstantOperand(0);
if (type.isIntType()) return new OPT_IntConstantOperand(0);
if (type.isShortType()) return new OPT_IntConstantOperand(0);
if (type.isLongType()) return new OPT_LongConstantOperand(0);
if (type.isFloatType()) return new OPT_FloatConstantOperand(0f);
if (type.isDoubleType()) return new OPT_DoubleConstantOperand(0.0);
return new OPT_NullConstantOperand();
}
/**
* Returns the correct operator for moving the given data type.
*
* @param type desired type to move
* @return the OPT_Operator to use for moving a value of the given type
*/
public static OPT_Operator getMoveOp(VM_TypeReference type) {
if (type.isLongType()) return LONG_MOVE;
if (type.isFloatType()) return FLOAT_MOVE;
if (type.isDoubleType()) return DOUBLE_MOVE;
if (type == VM_TypeReference.VALIDATION_TYPE) return GUARD_MOVE;
if (type.isReferenceType() || type.isWordType()) return REF_MOVE;
return INT_MOVE;
}
/**
* Returns the correct operator for a conditional move with the given data
* type.
*
* @param type desired type to move
* @return the OPT_Operator to use for moving a value of the given type
*/
public static OPT_Operator getCondMoveOp(VM_TypeReference type) {
if (type.isLongType()) return LONG_COND_MOVE;
if (type.isFloatType()) return FLOAT_COND_MOVE;
if (type.isDoubleType()) return DOUBLE_COND_MOVE;
if (type == VM_TypeReference.VALIDATION_TYPE) return GUARD_COND_MOVE;
if (type.isReferenceType() || type.isWordType()) return REF_COND_MOVE;
return INT_COND_MOVE;
}
/**
* Returns the correct operator for loading from the given field
*
* @param field field to load from
* @param isStatic is the field static
* @return the OPT_Operator to use when loading the given field
*/
public static OPT_Operator getLoadOp(VM_FieldReference field, boolean isStatic) {
return getLoadOp(field.getFieldContentsType(), isStatic);
}
/**
* Returns the correct operator for loading a value of the given type
*
* @param type type of value to load
* @param isStatic is the field static
* @return the OPT_Operator to use when loading the given field
*/
public static OPT_Operator getLoadOp(VM_TypeReference type, boolean isStatic) {
if (!VM_Configuration.LittleEndian && isStatic) {
// Handle the statics table hold subword values in ints
if (type.isByteType()) return INT_LOAD;
if (type.isBooleanType()) return INT_LOAD;
if (type.isCharType()) return INT_LOAD;
if (type.isShortType()) return INT_LOAD;
}
if (type.isByteType()) return BYTE_LOAD;
if (type.isBooleanType()) return UBYTE_LOAD;
if (type.isCharType()) return USHORT_LOAD;
if (type.isShortType()) return SHORT_LOAD;
if (type.isLongType()) return LONG_LOAD;
if (type.isFloatType()) return FLOAT_LOAD;
if (type.isDoubleType()) return DOUBLE_LOAD;
if (type.isReferenceType()) return REF_LOAD;
if (type.isWordType()) return REF_LOAD;
return INT_LOAD;
}
/**
* Returns the correct operator for storing to the given field.
*
* @param field The field we're asking about
* @param isStatic is the field static
* @return the OPT_Operator to use when storing to the given field
*/
public static OPT_Operator getStoreOp(VM_FieldReference field, boolean isStatic) {
return getStoreOp(field.getFieldContentsType(), isStatic);
}
/**
* Returns the correct operator for storing a value of the given type
*
* @param type desired type to store
* @param isStatic is the field static
* @return the OPT_Operator to use when storing to the given field
*/
public static OPT_Operator getStoreOp(VM_TypeReference type, boolean isStatic) {
if (!VM_Configuration.LittleEndian && isStatic) {
// Handle the statics table hold subword values in ints
if (type.isByteType()) return INT_STORE;
if (type.isBooleanType()) return INT_STORE;
if (type.isCharType()) return INT_STORE;
if (type.isShortType()) return INT_STORE;
}
if (type.isByteType()) return BYTE_STORE;
if (type.isBooleanType()) return BYTE_STORE;
if (type.isCharType()) return SHORT_STORE;
if (type.isShortType()) return SHORT_STORE;
if (type.isLongType()) return LONG_STORE;
if (type.isFloatType()) return FLOAT_STORE;
if (type.isDoubleType()) return DOUBLE_STORE;
if (type.isReferenceType()) return REF_STORE;
if (type.isWordType()) return REF_STORE;
return INT_STORE;
}
/**
* Generates an instruction to move the given operand into a register, and
* inserts it before the given instruction.
*
* @param pool register pool to allocate from
* @param s instruction to insert before
* @param op operand to copy to a register
* @return register operand that we copied into
*/
public static OPT_RegisterOperand moveIntoRegister(OPT_RegisterPool pool, OPT_Instruction s, OPT_Operand op) {
if (op instanceof OPT_RegisterOperand) {
return (OPT_RegisterOperand) op;
}
VM_TypeReference type = op.getType();
OPT_Operator move_op = OPT_IRTools.getMoveOp(type);
return moveIntoRegister(type, move_op, pool, s, op);
}
/**
* Generates an instruction to move the given operand into a register, and
* inserts it before the given instruction.
*
* @param type type to move
* @param move_op move operator to use
* @param pool register pool to allocate from
* @param s instruction to insert before
* @param op operand to copy to a register
* @return last use register operand that we copied into
*/
public static OPT_RegisterOperand moveIntoRegister(VM_TypeReference type, OPT_Operator move_op, OPT_RegisterPool pool,
OPT_Instruction s, OPT_Operand op) {
OPT_RegisterOperand rop = pool.makeTemp(type);
s.insertBefore(Move.create(move_op, rop, op));
rop = rop.copyD2U();
return rop;
}
/**
* Moves the 'from' instruction to immediately before the 'to' instruction.
*
* @param from instruction to move
* @param to instruction after where you want it moved
*/
public static void moveInstruction(OPT_Instruction from, OPT_Instruction to) {
from.remove();
to.insertBefore(from);
}
/**
* Inserts the instructions in the given basic block after the given
* instruction.
*
* @param after instruction after where you want it inserted
* @param temp basic block which contains the instructions to be inserted.
*/
public static void insertInstructionsAfter(OPT_Instruction after, OPT_BasicBlock temp) {
if (temp.isEmpty()) return;
OPT_Instruction after_after = after.getNext();
after.linkWithNext(temp.firstRealInstruction());
if (after_after == null) {
temp.lastRealInstruction().setNext(null);
} else {
temp.lastRealInstruction().linkWithNext(after_after);
}
}
/**
* Make an empty basic block on an edge in the control flow graph,
* and fix up the control flow graph and IR instructions accordingly.
*
* This routine will create the control struture
* <pre>
* in -> bb -> out.
* </pre>
* <em> Precondition </em>: There is an edge in the control flow graph
* from * in -> out.
*
* @param in the source of the control flow edge
* @param out the sink of the control flow edge
* @param ir the governing IR
* @return the new basic block bb
*/
public static OPT_BasicBlock makeBlockOnEdge(OPT_BasicBlock in, OPT_BasicBlock out, OPT_IR ir) {
// 1. Create the new basic block
OPT_BasicBlock bb = in.createSubBlock(out.firstInstruction().bcIndex, ir);
// 2. Splice the new basic block into the code order
OPT_BasicBlock next = in.nextBasicBlockInCodeOrder();
if (next == null) {
ir.cfg.addLastInCodeOrder(bb);
} else {
ir.cfg.breakCodeOrder(in, next);
ir.cfg.linkInCodeOrder(in, bb);
ir.cfg.linkInCodeOrder(bb, next);
}
// 3. update in's branch instructions
boolean foundGoto = false;
OPT_BranchOperand target = bb.makeJumpTarget();
OPT_BranchOperand outTarget = out.makeJumpTarget();
for (OPT_InstructionEnumeration e = in.reverseRealInstrEnumerator(); e.hasMoreElements();) {
OPT_Instruction s = e.next();
if (IfCmp2.conforms(s)) {
if (IfCmp2.getTarget1(s).similar(outTarget)) {
IfCmp2.setTarget1(s, (OPT_BranchOperand) target.copy());
}
if (IfCmp2.getTarget2(s).similar(outTarget)) {
IfCmp2.setTarget2(s, (OPT_BranchOperand) target.copy());
}
} else if (IfCmp.conforms(s)) {
if (IfCmp.getTarget(s).similar(outTarget)) {
IfCmp.setTarget(s, (OPT_BranchOperand) target.copy());
}
} else if (InlineGuard.conforms(s)) {
if (InlineGuard.getTarget(s).similar(outTarget)) {
InlineGuard.setTarget(s, (OPT_BranchOperand) target.copy());
}
} else if (Goto.conforms(s)) {
foundGoto = true;
if (Goto.getTarget(s).similar(outTarget)) {
Goto.setTarget(s, (OPT_BranchOperand) target.copy());
}
} else if (TableSwitch.conforms(s)) {
foundGoto = true;
if (TableSwitch.getDefault(s).similar(outTarget)) {
TableSwitch.setDefault(s, (OPT_BranchOperand) target.copy());
}
for (int i = 0; i < TableSwitch.getNumberOfTargets(s); i++) {
if (TableSwitch.getTarget(s, i).similar(outTarget)) {
TableSwitch.setTarget(s, i, (OPT_BranchOperand) target.copy());
}
}
} else if (LowTableSwitch.conforms(s)) {
foundGoto = true;
for (int i = 0; i < LowTableSwitch.getNumberOfTargets(s); i++) {
if (LowTableSwitch.getTarget(s, i).similar(outTarget)) {
LowTableSwitch.setTarget(s, i, (OPT_BranchOperand) target.copy());
}
}
} else if (LookupSwitch.conforms(s)) {
foundGoto = true;
if (LookupSwitch.getDefault(s).similar(outTarget)) {
LookupSwitch.setDefault(s, (OPT_BranchOperand) target.copy());
}
for (int i = 0; i < LookupSwitch.getNumberOfTargets(s); i++) {
if (LookupSwitch.getTarget(s, i).similar(outTarget)) {
LookupSwitch.setTarget(s, i, (OPT_BranchOperand) target.copy());
}
}
} else {
// done processing all branches
break;
}
}
// 4. Add a goto bb->out
OPT_Instruction s = Goto.create(GOTO, out.makeJumpTarget());
bb.appendInstruction(s);
// add goto in->next
// if out was not the fallthrough, add a GOTO to preserve this
// control flow
if (out != next) {
// if there's already a GOTO, there's no fall through
if (!foundGoto) {
/*
* TODO: come up with a better fix (?).
*
* This is a fix to a particular problem in dacapo xalan.
*
* We have a loop inside an exception handler, and the exception handler
* is empty. The loop termination condition simply falls through the
* exception handler to the next block. This works fine until LeaveSSA,
* when we split the final block and insert a GOTO to the exception handler
* block. When we reassemble the IR afterwards, kaboom.
*
* I would have though it better not to fall through empty exception handlers
* at all, and explicitly GOTO past them from the get go. RJG 4/2/7
*/
OPT_BasicBlock jumpTarget = next;
while (jumpTarget.isEmpty() && jumpTarget.isExceptionHandlerBasicBlock()) {
jumpTarget = jumpTarget.nextBasicBlockInCodeOrder();
}
s = Goto.create(GOTO, jumpTarget.makeJumpTarget());
in.appendInstruction(s);
}
}
// 5. Update the CFG
in.recomputeNormalOut(ir);
bb.recomputeNormalOut(ir);
return bb;
}
/**
* Is the operand u, which is a use in instruction s, also a def
* in instruction s? That is, is this operand defined as a DU operand
* in InstructionFormatList.dat.
*
* TODO!!: This implementation is slow. Think about adding
* some IR support for this functionality; possibly add methods like
* enumeratePureDefs(), enumerateImpureUses(), etc ..., and restructure
* the caller to avoid having to call this function. Not going
* to put effort into this now, as the whole scratch register
* architecture has a questionable future.
*/
public static boolean useDoublesAsDef(OPT_Operand u, OPT_Instruction s) {
for (Enumeration<OPT_Operand> d = s.getDefs(); d.hasMoreElements();) {
OPT_Operand def = d.nextElement();
if (def != null) {
if (def == u) return true;
}
}
return false;
}
/**
* Is the operand d, which is a def in instruction s, also a def
* in instruction s? That is, is this operand defined as a DU operand
* in InstructionFormatList.dat.
*
* TODO!!: This implementation is slow. Think about adding
* some IR support for this functionality; possibly add methods like
* enumeratePureDefs(), enumerateImpureUses(), etc ..., and restructure
* the caller to avoid having to call this function. Not going
* to put effort into this now, as the whole scratch register
* architecture has a questionable future.
*/
public static boolean defDoublesAsUse(OPT_Operand d, OPT_Instruction s) {
for (Enumeration<OPT_Operand> u = s.getUses(); u.hasMoreElements();) {
OPT_Operand use = u.nextElement();
if (use != null) {
if (use.similar(d)) return true;
}
}
return false;
}
/**
* Does instruction s define register r?
*/
public static boolean definedIn(OPT_Register r, OPT_Instruction s) {
for (Enumeration<OPT_Operand> e = s.getDefs(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null && op.isRegister()) {
if (op.asRegister().getRegister().number == r.number) {
return true;
}
}
}
return false;
}
/**
* Does instruction s use register r?
*/
public static boolean usedIn(OPT_Register r, OPT_Instruction s) {
for (Enumeration<OPT_Operand> e = s.getUses(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null && op.isRegister()) {
if (op.asRegister().getRegister().number == r.number) {
return true;
}
}
}
return false;
}
/**
* Mark the parameter as nonGC and nonPEI and return it.
* To be used in passthrough expressions like
* <pre>
* instr.insertBack(notPEIGC(Load.create(...)));
* </pre>
*
* @param instr the given instruction
* @return the given instruction
*/
public static OPT_Instruction nonPEIGC(OPT_Instruction instr) {
instr.markAsNonPEINonGCPoint();
return instr;
}
/**
* Might this instruction be a load from a field that is declared
* to be volatile?
*
* @param s the insruction to check
* @return <code>true</code> if the instruction might be a load
* from a volatile field or <code>false</code> if it
* cannot be a load from a volatile field
*/
public static boolean mayBeVolatileFieldLoad(OPT_Instruction s) {
return s.mayBeVolatileFieldLoad();
}
}