/* * 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.controlflow; import static org.jikesrvm.compilers.opt.ir.Operators.ARCH_INDEPENDENT_END_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_ADD_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.INT_SUB_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.LABEL; import java.util.ArrayList; import java.util.Enumeration; import org.jikesrvm.VM; import org.jikesrvm.compilers.opt.DefUse; import org.jikesrvm.compilers.opt.OptimizingCompilerException; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.BasicBlockEnumeration; import org.jikesrvm.compilers.opt.ir.Binary; import org.jikesrvm.compilers.opt.ir.BoundsCheck; import org.jikesrvm.compilers.opt.ir.GuardResultCarrier; import org.jikesrvm.compilers.opt.ir.GuardedBinary; import org.jikesrvm.compilers.opt.ir.GuardedUnary; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.IREnumeration; import org.jikesrvm.compilers.opt.ir.IfCmp; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.InstructionFormat; import org.jikesrvm.compilers.opt.ir.Label; import org.jikesrvm.compilers.opt.ir.Move; import org.jikesrvm.compilers.opt.ir.NullCheck; import org.jikesrvm.compilers.opt.ir.Operator; import org.jikesrvm.compilers.opt.ir.Phi; import org.jikesrvm.compilers.opt.ir.RegisterOperandEnumeration; import org.jikesrvm.compilers.opt.ir.ResultCarrier; import org.jikesrvm.compilers.opt.ir.Unary; import org.jikesrvm.compilers.opt.ir.operand.BasicBlockOperand; import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand; import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand; import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand; import org.jikesrvm.compilers.opt.ir.operand.Operand; import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; import org.jikesrvm.compilers.opt.ssa.GCP; import org.jikesrvm.compilers.opt.util.GraphNode; import org.jikesrvm.util.BitVector; /** * <p>A node in the LST (Loop Structure Tree) with added information * on: * * <ul><li>Whether this is a countable, affine or non-regular loop</li> * <li>The registers being used to hold the loop iterator</li> * <li>The initial loop iterator value</li> * <li>The terminal loop iterator value</li> * <li>The instruction that modifies the iterator</li> * <li>The phi instruction that merges the redefined iterator with its original value</li> * <li>The condition used to test for loop termination</li> * <li>The stride operand</li> * </ul> * * The information is only held on regular loops. The regular loop * structure is: * * <listing> * predecessor: * initialLoopIterator = ...; * header: * phiLoopIterator = phi (initialLoopIterator, carriedLoopIterator) * ...body1... * carriedLoopIterator = phiLoopIterator iteratorInstr.opcode stride; * ...body2... * exit: * if carriedLoopIterator condition terminalIteratorValue goto header * successor: * </listing> * * While loops (and implicitly for loops) aren't handled as they can * be transformed to this form by {@link CFGTransformations}. * * TODO: * <ul><li>More complex iterator instructions (sequences rather than single instructions)</li> * <li>Handle longs, floats and doubles as loop iterators</li> * <li>Consideration of more loop structures</li> * </ul> * </p> * * @see LSTNode */ public final class AnnotatedLSTNode extends LSTNode { // -oO Debug information Oo- /** * Flag to optionally print verbose debugging messages */ private static final boolean DEBUG = false; // -oO Cached values for convenience Oo- /** * A pointer to the governing IR */ private final IR ir; // -oO Blocks that get set up during the recognition of the loop Oo- /** * The out of loop block before the header */ public BasicBlock predecessor; // N.B. the header is defined in the superclass /** * The in loop block that either loops or leaves the loop */ public BasicBlock exit; /** * The out of loop block following the exit block */ public BasicBlock successor; // -oO Instructions that get set up during the recognition of the loop Oo- /** * The if instruction within the exit block */ private Instruction ifCmpInstr; /** * The instruction that modifies the iterator */ private Instruction iteratorInstr; // Values that get determined during the recognition of the loop /** * The the initial iterator that comes into the phi node in the header */ public Operand initialIteratorValue; /** * The iterator that is used to loop within the exit block */ private Operand carriedLoopIterator; /** * The the phi iterator that gets modified by the stride to produce the carried iterator */ private Operand phiLoopIterator; /** * The value that ends the loop */ public Operand terminalIteratorValue; /** * The condition that is used to check for the end of loop */ public ConditionOperand condition; /** * The stride operand to the iterator instruction */ public Operand strideValue; // -oO Interfaces to the rest of the compiler Oo- /** * Constructor * * @param ir The containing IR * @param node The node that's being annotated */ public AnnotatedLSTNode(IR ir, LSTNode node) { // Clone information from non-annotated node super(node); this.ir = ir; // Process inner loops Enumeration<GraphNode> innerLoops = node.outNodes(); // Iterate over loops contained within this loop annotating from the inside out while (innerLoops.hasMoreElements()) { AnnotatedLSTNode nestedLoop = new AnnotatedLSTNode(ir, (LSTNode) innerLoops.nextElement()); insertOut(nestedLoop); } // Annotate node perform(); } /** * Is this a countable loop of the form: * <listing> * predecessor: * initialLoopIterator = ConstantInitialValue; * header: * phiLoopIterator = phi (initialLoopIterator, carriedLoopIterator) * ...body1... * carriedLoopIterator = phiLoopIterator (+|-) ConstantStride; * ...body2... * exit: * if carriedLoopIterator condition ConstantTerminalIteratorValue goto header * successor: * </listing> * ie. lots of constant fields so we can calculate the number of * loop iterations (handy for pre-scheduling). * * @return Whether this a countable loop or not */ public boolean isCountableLoop() { return (initialIteratorValue != null) && isConstant(initialIteratorValue) && (terminalIteratorValue != null) && isConstant(terminalIteratorValue) && (strideValue != null) && isConstant(strideValue) && (iteratorInstr != null) && ((iteratorInstr.operator.opcode == INT_ADD_opcode) || (iteratorInstr.operator.opcode == INT_SUB_opcode)); } /** * Is this an affine loop of the form: * <listing> * predecessor: * initialLoopIterator = ...; * header: * phiLoopIterator = phi (initialLoopIterator, carriedLoopIterator) * ...body1... * carriedLoopIterator = phiLoopIterator (+|-) invariantStride; * ...body2... * exit: * if carriedLoopIterator condition invariantTerminalIteratorValue goto header * successor: * </listing> * ie. lots of constant fields so we can calculate the number of * loop iterations (handy for pre-scheduling). * * @return Whether this an affine loop or not */ public boolean isAffineLoop() { return (initialIteratorValue != null) && isLoopInvariant(initialIteratorValue, loop, header) && (terminalIteratorValue != null) && isLoopInvariant(terminalIteratorValue, loop, header) && (strideValue != null) && isLoopInvariant(strideValue, loop, header) && (iteratorInstr != null) && ((iteratorInstr.operator.opcode == INT_ADD_opcode) || (iteratorInstr.operator.opcode == INT_SUB_opcode)); } /** * Is this loop a non-regular loop? * * @return Whether this is a non-regular loop */ public boolean isNonRegularLoop() { return !isAffineLoop(); } /** * Is this value modified by the loop? * * @return whether the value is modified */ public boolean isInvariant(Operand op) { return isLoopInvariant(op, loop, header); } /** * Is this operand related to the iterator of this loop? * * @param op Operand to test * @return whether related to iterator (initial, phi or carried) */ public boolean isRelatedToIterator(Operand op) { return isFixedDistanceFromPhiIterator(op); } /** * Is this operand related to the phi iterator of this loop? * @param op Operand to test * @return whether related to iterator (phi) */ public boolean isPhiLoopIterator(Operand op) { return op.similar(phiLoopIterator); } /** * Is this operand related to the carried iterator of this loop? * @param op Operand to test * @return whether related to iterator (carried) */ public boolean isCarriedLoopIterator(Operand op) { return op.similar(carriedLoopIterator); } /** * Is the loop iterator monotonic? */ public boolean isMonotonic() { return isConstant(strideValue); } /** * Return the stride value for monotonic loops * * @return the constant stride value */ public int getMonotonicStrideValue() { if (iteratorInstr.operator.opcode == INT_SUB_opcode) { return -((IntConstantOperand) strideValue).value; } else if (iteratorInstr.operator.opcode == INT_ADD_opcode) { return ((IntConstantOperand) strideValue).value; } else { throw new Error("Error reading stride value"); } } /** * Is the loop iterator a monotonic increasing value */ public boolean isMonotonicIncreasing() { if ((!isMonotonic()) || condition.isGREATER() || condition.isGREATER_EQUAL() || condition.isHIGHER() || condition.isHIGHER_EQUAL()) { return false; } else { return getMonotonicStrideValue() > 0; } } /** * Is the loop iterator a monotonic decreasing value */ public boolean isMonotonicDecreasing() { if ((!isMonotonic()) || condition.isLESS() || condition.isLESS_EQUAL() || condition.isLOWER() || condition.isLOWER_EQUAL()) { return false; } else { return getMonotonicStrideValue() < 0; } } /** * Does this basic block appear in the loop? */ public boolean contains(BasicBlock block) { return (block.getNumber() < loop.length()) && loop.get(block.getNumber()); } // -oO Utility methods Oo- /** * Converts the annotated loop to a concise string */ public String toString() { String ifCmpString = "??"; if ((ifCmpInstr != null) && (IfCmp.conforms(ifCmpInstr))) { ifCmpString = IfCmp.getCond(ifCmpInstr).toString(); } return ("// pred: " + predecessor + "\n" + "loop : " + initialIteratorValue + ";\n" + "head {" + header + "}:\n" + " " + phiLoopIterator + "=phi(" + initialIteratorValue + "," + carriedLoopIterator + ");\n" + " " + carriedLoopIterator + "=" + phiLoopIterator + "+" + strideValue + ";\n" + "// blocks: " + loop + "\n" + "exit {" + exit + "}:\n" + " if(" + carriedLoopIterator + " " + ifCmpString + " " + terminalIteratorValue + ")\n" + " goto head;\n" + "// succ: " + successor + "\n"); } /** * Dump a human readable description of the loop */ public void dump() { VM.sysWrite("********* START OF SSA LOOP DUMP in AnnotatedLSTNode FOR " + ir.method + "\n"); if (isNonRegularLoop()) { VM.sysWrite("Non-regular"); } else if (isAffineLoop()) { VM.sysWrite("Affine"); } else if (isCountableLoop()) { VM.sysWrite("Countable"); } else { VM.sysWrite("INVALID"); } VM.sysWrite(" Loop:\n\tIteratorInstr: " + iteratorInstr.toString() + "\n\tIfCmpInstr:" + ifCmpInstr.toString() + "\n\tTerminalIteratorValue: " + terminalIteratorValue.toString() + "\n\tInitialIteratorValue: " + initialIteratorValue.toString() + "\n\tCarriedLoopIterator: " + carriedLoopIterator.toString() + "\n\tPhiLoopIterator: " + phiLoopIterator.toString() + "\n\tStrideValue: " + strideValue.toString() + "\n\tLoop: " + getBasicBlocks().toString() + " / " + loop.toString() + "\n"); BasicBlockEnumeration loopBlocks = getBasicBlocks(); // loop_over_basic_blocks: while (loopBlocks.hasMoreElements()) { // The current basic block BasicBlock curLoopBB = loopBlocks.next(); dumpBlock(curLoopBB); } VM.sysWrite("********* END OF SSA LOOP DUMP in AnnotatedLSTNode FOR " + ir.method + "\n"); } /** * Dump a human readable description of a basic block within the loop * * @param block The basic block to dump */ void dumpBlock(BasicBlock block) { if (block == header) { VM.sysWrite("Header "); } if (block == exit) { VM.sysWrite("Exit "); } VM.sysWrite("Block #" + block.getNumber() + ":\n"); // Print the instructions IREnumeration.AllInstructionsEnum instructions = new IREnumeration.AllInstructionsEnum(ir, block); while (instructions.hasMoreElements()) { Instruction instr = instructions.next(); dumpInstruction(ir, instr); } } /** * Dump a human readable description of an instruction within a * basic block within the loop * * @param ir Containing IR * @param instr The instruction to dump */ static void dumpInstruction(IR ir, Instruction instr) { VM.sysWrite(instructionToString(ir, instr)); } /** * Convert instruction to String in of AnnotatedLSTNode format * * @param ir Containing IR * @param instr The instruction to dump */ static String instructionToString(IR ir, Instruction instr) { StringBuilder sb = new StringBuilder(); sb.append(instr.bcIndex).append("\t").append(instr.isPEI() ? "E" : " ").append(instr.isGCPoint() ? "G " : " "); if (instr.operator == LABEL) { sb.append("LABEL").append(Label.getBlock(instr).block.getNumber()); if (Label.getBlock(instr).block.getInfrequent()) { sb.append(" (Infrequent)"); } } else { IREnumeration.AllDefsEnum defs = new IREnumeration.AllDefsEnum(ir, instr); IREnumeration.AllUsesEnum uses = new IREnumeration.AllUsesEnum(ir, instr); sb.append(instr.operator).append("\n\t defs: "); while (defs.hasMoreElements()) { sb.append(defs.next().toString()); if (defs.hasMoreElements()) { sb.append(", "); } } sb.append("\n\t uses: "); while (uses.hasMoreElements()) { sb.append(uses.next().toString()); if (uses.hasMoreElements()) { sb.append(", "); } } } sb.append("\n"); return sb.toString(); } /** * Test whether the operand is constant * * @param op Operand to determine whether it's constant * @return Is the operand constant */ static boolean isConstant(Operand op) { return op instanceof IntConstantOperand; } /** * Is this operand a fixed distance from the phi iterator? * * @param op the operand to test * @return whether or not it is a fixed distance */ boolean isFixedDistanceFromPhiIterator(Operand op) { if (op.similar(phiLoopIterator)) { return true; } else { Instruction opInstr = definingInstruction(op); if ((opInstr.operator.opcode == INT_ADD_opcode) || (opInstr.operator.opcode == INT_SUB_opcode)) { Operand val1 = Binary.getVal1(opInstr); Operand val2 = Binary.getVal2(opInstr); return ((val1.isConstant() && isFixedDistanceFromPhiIterator(val2)) || (val2.isConstant() && isFixedDistanceFromPhiIterator(val1))); } else { return false; } } } /** * Get fixed distance from the phi iterator * * @param op the operand to test * @return the fixed distance */ public int getFixedDistanceFromPhiIterator(Operand op) { if (op.similar(phiLoopIterator)) { return 0; } else { Instruction opInstr = definingInstruction(op); if (opInstr.operator.opcode == INT_ADD_opcode) { Operand val1 = Binary.getVal1(opInstr); Operand val2 = Binary.getVal2(opInstr); if (val1.isConstant()) { return val1.asIntConstant().value + getFixedDistanceFromPhiIterator(val2); } else { VM._assert(val2.isConstant()); return getFixedDistanceFromPhiIterator(val1) + val2.asIntConstant().value; } } else if (opInstr.operator.opcode == INT_SUB_opcode) { Operand val1 = Binary.getVal1(opInstr); Operand val2 = Binary.getVal2(opInstr); if (val1.isConstant()) { return val1.asIntConstant().value - getFixedDistanceFromPhiIterator(val2); } else { VM._assert(val2.isConstant()); return getFixedDistanceFromPhiIterator(val1) - val2.asIntConstant().value; } } } throw new Error("Value isn't fixed distance from phi iterator"); } /** * Test whether operand value will be invariant in a loop by tracing * back earlier definitions. There is similar code in {@link * LoopUnrolling}. * * @param op Operand to determine whether it's invariant * @param loop Loop in which we wish to know the invariance of the operand * @param header The loop header for determining if PEIs are invariant * @return Whether the operand is invariant or not */ private static boolean isLoopInvariant(Operand op, BitVector loop, BasicBlock header) { boolean result; if (op.isConstant()) { result = true; } else if (op.isRegister()) { Instruction instr = definingInstruction(op); // Is the instruction that defined this register in the loop? if (!CFGTransformations.inLoop(instr.getBasicBlock(), loop)) { // No => the value is invariant in the loop result = true; } else { // Trace op to where invariant/variant values may be defined switch (instr.operator().format) { case InstructionFormat.Binary_format: result = (isLoopInvariant(Binary.getVal1(instr), loop, header) && isLoopInvariant(Binary.getVal2(instr), loop, header)); break; case InstructionFormat.BoundsCheck_format: if (isLoopInvariant(BoundsCheck.getRef(instr), loop, header) && isLoopInvariant(BoundsCheck.getIndex(instr), loop, header)) { // Iterate over instructions before the null check Instruction lastInst; if (header == instr.getBasicBlock()) { lastInst = instr; } else { lastInst = header.lastInstruction(); } result = false; Instruction itrInst; for (itrInst = header.firstRealInstruction(); itrInst != lastInst; itrInst = itrInst.nextInstructionInCodeOrder()) { // Check it would be safe for this nullcheck to before // the loop header without changing the exception // semantics if (BoundsCheck.conforms(itrInst) && BoundsCheck.getRef(itrInst).similar(BoundsCheck.getRef(instr)) && BoundsCheck.getIndex(itrInst).similar(BoundsCheck.getIndex(instr))) { // it's safe if there's already an equivalent null check result = true; break; } else if (itrInst.isAllocation() || itrInst.isDynamicLinkingPoint() || (itrInst.operator.opcode >= ARCH_INDEPENDENT_END_opcode) || itrInst.isPEI() || itrInst.isThrow() || itrInst.isImplicitLoad() || itrInst.isImplicitStore() || GCP.usesOrDefsPhysicalRegisterOrAddressType(itrInst)) { // it's not safe in these circumstances (see LICM) if (DEBUG) { VM.sysWriteln("null check not invariant: " + itrInst); } break; } } if (itrInst == instr) { // did we iterate to the end of the instructions and // find instr result = true; } } else { result = false; } break; case InstructionFormat.GuardedBinary_format: result = (isLoopInvariant(GuardedBinary.getVal1(instr), loop, header) && isLoopInvariant(GuardedBinary.getVal2(instr), loop, header) && isLoopInvariant(GuardedBinary.getGuard(instr), loop, header)); break; case InstructionFormat.GuardedUnary_format: result = (isLoopInvariant(GuardedUnary.getVal(instr), loop, header) && isLoopInvariant(GuardedUnary.getGuard(instr), loop, header)); break; case InstructionFormat.Move_format: result = isLoopInvariant(Move.getVal(instr), loop, header); break; case InstructionFormat.NullCheck_format: if (isLoopInvariant(NullCheck.getRef(instr), loop, header)) { // Iterate over instructions before the null check Instruction lastInst; if (header == instr.getBasicBlock()) { lastInst = instr; } else { lastInst = header.lastInstruction(); } result = false; Instruction itrInst; for (itrInst = header.firstRealInstruction(); itrInst != lastInst; itrInst = itrInst.nextInstructionInCodeOrder()) { // Check it would be safe for this nullcheck to before // the loop header without changing the exception // semantics if (NullCheck.conforms(itrInst) && NullCheck.getRef(itrInst).similar(NullCheck.getRef(instr))) { // it's safe if there's already an equivalent null check result = true; break; } else if (itrInst.isAllocation() || itrInst.isDynamicLinkingPoint() || (itrInst.operator.opcode >= ARCH_INDEPENDENT_END_opcode) || itrInst.isPEI() || itrInst.isThrow() || itrInst.isImplicitLoad() || itrInst.isImplicitStore() || GCP.usesOrDefsPhysicalRegisterOrAddressType(itrInst)) { // it's not safe in these circumstances (see LICM) if (DEBUG) { VM.sysWriteln("null check not invariant: " + itrInst); } break; } } if (itrInst == instr) { // did we iterate to the end of the instructions and // find instr result = true; } } else { result = false; } break; case InstructionFormat.Unary_format: result = isLoopInvariant(Unary.getVal(instr), loop, header); break; default: // Unknown instruction format so leave result = false; break; } } } else { // Other operand types result = false; } if (DEBUG) { VM.sysWriteln("isLoopInvariant: " + op + (result ? " true" : " false")); } return result; } /** * Loop invariants may not be accessible before a loop, so generate * the instructions so they are * * @param block to generate instructions into * @param op the operand we hope to use before the loop */ public Operand generateLoopInvariantOperand(BasicBlock block, Operand op) { Instruction instr = definingInstruction(op); if (op.isConstant() || !CFGTransformations.inLoop(instr.getBasicBlock(), loop)) { // the operand is already invariant return op; } else { RegisterOperand result; Instruction opInstr = definingInstruction(op); // create result of correct type if (ResultCarrier.conforms(opInstr)) { result = ResultCarrier.getResult(opInstr).copyRO(); result.setRegister(ir.regpool.getReg(result)); } else { if (VM.VerifyAssertions) VM._assert(GuardResultCarrier.conforms(opInstr)); result = GuardResultCarrier.getGuardResult(opInstr).copyRO(); result.setRegister(ir.regpool.getReg(result)); } Instruction resultInstruction; Operator operator = instr.operator; switch (operator.format) { case InstructionFormat.Binary_format: resultInstruction = Binary.create(operator, result, generateLoopInvariantOperand(block, Binary.getVal1(instr)), generateLoopInvariantOperand(block, Binary.getVal2(instr))); break; case InstructionFormat.BoundsCheck_format: resultInstruction = BoundsCheck.create(operator, result, generateLoopInvariantOperand(block, BoundsCheck.getRef(instr)), generateLoopInvariantOperand(block, BoundsCheck.getIndex(instr)), generateLoopInvariantOperand(block, BoundsCheck.getGuard(instr))); break; case InstructionFormat.GuardedBinary_format: resultInstruction = GuardedBinary.create(operator, result, generateLoopInvariantOperand(block, GuardedBinary.getVal1(instr)), generateLoopInvariantOperand(block, GuardedBinary.getVal2(instr)), generateLoopInvariantOperand(block, GuardedBinary.getGuard(instr))); break; case InstructionFormat.GuardedUnary_format: resultInstruction = GuardedUnary.create(operator, result, generateLoopInvariantOperand(block, GuardedUnary.getVal(instr)), generateLoopInvariantOperand(block, GuardedUnary.getGuard(instr))); break; case InstructionFormat.Move_format: resultInstruction = Move.create(operator, result, generateLoopInvariantOperand(block, Move.getVal(instr))); break; case InstructionFormat.NullCheck_format: resultInstruction = NullCheck.create(operator, result, generateLoopInvariantOperand(block, NullCheck.getRef(instr))); break; case InstructionFormat.Unary_format: resultInstruction = Unary.create(operator, result, generateLoopInvariantOperand(block, Unary.getVal(instr))); break; default: // Unknown instruction format so leave throw new Error("TODO: generate loop invariant for operator " + operator); } resultInstruction.copyPosition(instr); block.appendInstruction(resultInstruction); DefUse.updateDUForNewInstruction(resultInstruction); return result.copyRO(); } } /** * Follow the operand's definition filtering out moves * This code is taken and modified from an old {@link LoopUnrolling} * * @param use Operand to follow * @return the first defintion of the instruction which isn't a move */ public static Operand follow(Operand use) { while (true) { // Are we still looking at a register operand? if (!use.isRegister()) { // No - we're no longer filtering out moves then break; } // Get definitions of register RegisterOperand rop = use.asRegister(); RegisterOperandEnumeration defs = DefUse.defs(rop.getRegister()); // Does register have definitions? if (!defs.hasMoreElements()) { // No - Register musn't be defined in this block break; } // Get the 1st instruction that defines the register use = defs.next(); Instruction def = use.instruction; // Was the instruction that defined this register a move? if (!Move.conforms(def)) { // No - return the register operand from the defining instruction break; } // Does the register have multiple defintions? if (defs.hasMoreElements()) { // Yes - too complex to follow so let's leave break; } use = Move.getVal(def); } return use; } /** * Find the instruction that defines an operand. * If the operand is a register, return the instruction that defines it, else return the operands instruction * * @param op The operand we're searching for the definition of */ public static Instruction definingInstruction(Operand op) { Instruction result = op.instruction; // Is operand a register? if (op instanceof RegisterOperand) { // Yes - check the definitions out RegisterOperandEnumeration defs = DefUse.defs(((RegisterOperand) op).getRegister()); // Does this register have any defintions? if (!defs.hasMoreElements()) { // No - must have been defined in previous block so just return register } else { // Get the instruction that defines the register result = defs.next().instruction; // Check to see if there are any more definitions if (defs.hasMoreElements()) { // Multiple definitions of register, just return register to be safe result = op.instruction; } } } return result; } /** * Is the a particular block in this loop? * * @return true => block is in the loop, false => block not in loop */ public boolean isInLoop(BasicBlock block) { return CFGTransformations.inLoop(block, loop); } /** * Return an enumeration of basic blocks corresponding to a depth * first traversal of the blocks in the loops graphs * * @param block block to visit * @param bbs enumeration so far * @param blocksLeftToVisit blocks left to visit * @return Blocks in loop with header first and exit last */ private BBEnum getBasicBlocks(BasicBlock block, BBEnum bbs, BitVector blocksLeftToVisit) { if (block != exit) { bbs.add(block); } blocksLeftToVisit.clear(block.getNumber()); BasicBlockEnumeration successors = block.getNormalOut(); while (successors.hasMoreElements()) { block = successors.next(); if (blocksLeftToVisit.get(block.getNumber())) { getBasicBlocks(block, bbs, blocksLeftToVisit); } } return bbs; } /** * Return an enumeration of basic blocks corresponding to a depth * first traversal of the blocks in the loops graphs * * @return Blocks in loop with header first and exit last */ public BasicBlockEnumeration getBasicBlocks() { BitVector blocksLeftToVisit = new BitVector(loop); BBEnum bbs = getBasicBlocks(header, new BBEnum(), blocksLeftToVisit); if (exit != null) { // place the exit block at the end of the list if we've recognized one bbs.add(exit); } return bbs; } /** * Check the edges out of a block are within the loop * * @param block to check */ private void checkOutEdgesAreInLoop(BasicBlock block) throws NonRegularLoopException { // The blocks (copy of) that are branched to from this block BasicBlockEnumeration block_outEdges = block.getOut(); // Check that the blocks that we branch into are all inside the loop // loop_over_loop_body_block_out_edges: while (block_outEdges.hasMoreElements()) { BasicBlock curEdgeBB = block_outEdges.next(); // Is this block in the loop? if ((!isInLoop(curEdgeBB)) && (block != exit)) { // Block wasn't in the loop throw new NonRegularLoopException( "Parallelization giving up: edge out of block in loop to a block outside of the loop, and the block wasn't the loop exit" + ((block == header) ? " (it was the header block though)" : "")); } } // end of loop_over_loop_body_block_out_edges } /** * Check the edges into a block are from within the loop * * @param block to check */ private void checkInEdgesAreInLoop(BasicBlock block) throws NonRegularLoopException { // The blocks (copy of) that branch to this block BasicBlockEnumeration block_inEdges = block.getIn(); // Check that the blocks that branch into this one are all inside the loop too // loop_over_loop_body_block_in_edges: while (block_inEdges.hasMoreElements()) { BasicBlock curEdgeBB = block_inEdges.next(); // Is this block in the loop? if ((!isInLoop(curEdgeBB)) && (block != header)) { // Block wasn't in the loop throw new NonRegularLoopException( "Parallelization giving up: edge into a block in the loop from a block outside of the loop, and the block wasn't the loop header" + ((block == exit) ? " (it was the exit block though)" : "")); } } // end of loop_over_loop_body_block_in_edges } // -oO Perform annotation Oo- /** * Convert node into annotated format */ private void perform() throws OptimizingCompilerException { // Check we have a loop if (loop == null) { return; } // Process the header first as it's the most likely source of non-regularity try { processHeader(); // Get the basic blocks that constitute the loop BasicBlockEnumeration loopBlocks = getBasicBlocks(); // Loop over all blocks within this loop and calculate iterator.. information // loop_over_basic_blocks: while (loopBlocks.hasMoreElements()) { // The current basic block BasicBlock curLoopBB = loopBlocks.next(); // Is this block the loop header? if (curLoopBB == header) { // The header was already processed } else { processLoopBlock(curLoopBB); } } } catch (NonRegularLoopException e) { if (DEBUG) { VM.sysWrite(e.summary() + "\n"); } // Make sure this loop looks non-regular initialIteratorValue = null; } if (DEBUG && (!isNonRegularLoop())) { dump(); } } /** * Process the loop header basic block */ private void processHeader() throws NonRegularLoopException { // The blocks (copy of) that branch to this block BasicBlockEnumeration head_inEdges = header.getIn(); // Loop over blocks that branch to this one // loop_over_header_in_edges: while (head_inEdges.hasMoreElements()) { BasicBlock curEdgeBB = head_inEdges.next(); // Is this block in the loop? if (isInLoop(curEdgeBB)) { // Yes - must be the exit block if (exit != null) { // we already have an exit block so loop structure is too // complex for us to understand throw new NonRegularLoopException( "Multiple back edges to the header block making exit block undistinguishable."); } exit = curEdgeBB; processExit(); } else { // No - this is an out of loop block going into the header if (predecessor != null) { // we already have a predecessor to the header block, more // than 1 is beyond this optimisation at the moment throw new NonRegularLoopException( "Multiple out of loop edges into the header making predecessor block undistinguishable."); } predecessor = curEdgeBB; } } // end of loop_over_header_in_edges // If the header isn't the exit block, check it doesn't branch outside of the loop if (header != exit) { checkOutEdgesAreInLoop(header); } } /** * Process the loop exit basic block */ private void processExit() throws NonRegularLoopException { // If the exit isn't the header block, check it doesn't have in edges from outside the loop if (header != exit) { checkInEdgesAreInLoop(exit); } // Check the exit block leaves the loop BasicBlockEnumeration exitBlock_outEdges = exit.getOut(); boolean exits = false; // check_exit_block_exits: while (exitBlock_outEdges.hasMoreElements()) { BasicBlock curExitBlockOutEdgeBB = exitBlock_outEdges.next(); if (isInLoop(curExitBlockOutEdgeBB)) { // An in loop out edge from the exit block } else { // An out of loop edge from the exit block exits = true; successor = curExitBlockOutEdgeBB; if (successor == header) { throw new NonRegularLoopException("Unimplemented condition - see LoopUnrolling.java : 240"); } } } // end of check_exit_block_exits if (!exits) { throw new NonRegularLoopException( "Exit block (containing back edge to header) doesn't have an out of loop out edge."); } else { // Get the if instruction used to loop in the exit block ifCmpInstr = exit.firstBranchInstruction(); if (ifCmpInstr == null) { throw new NonRegularLoopException("Exit block branch doesn't have a (1st) branching instruction."); } else if (ifCmpInstr.operator.opcode != INT_IFCMP_opcode) { throw new NonRegularLoopException("branch is int_ifcmp but " + ifCmpInstr.operator + "\n"); } else { // Get the terminal and iterator operations carriedLoopIterator = follow(IfCmp.getVal1(ifCmpInstr)); terminalIteratorValue = follow(IfCmp.getVal2(ifCmpInstr)); condition = (ConditionOperand) IfCmp.getCond(ifCmpInstr).copy(); // Check we have them the right way around and that they do the job we expect { boolean iteratorInvariant = isLoopInvariant(carriedLoopIterator, loop, header); boolean terminalValueInvariant = isLoopInvariant(terminalIteratorValue, loop, header); // Is the iterator loop invariant? if (iteratorInvariant) { // Yes - Is the terminal value loop invariant? if (terminalValueInvariant) { // Yes - both parameters to the condition are invariant throw new NonRegularLoopException( "Exit block condition values are both invariant (single or infinite loop):\n" + "Loop = " + loop.toString() + "\nIterator = " + carriedLoopIterator + "\nTerminal = " + terminalIteratorValue); } else { // No - swap values over Operand temp = terminalIteratorValue; terminalIteratorValue = carriedLoopIterator; carriedLoopIterator = temp; } } else { // No - Is the terminal value loop invariant? if (terminalValueInvariant) { // Yes - this is the condition we hoped for } else { // No - both loop values are variant and loop is too complex to analyse throw new NonRegularLoopException("Exit block condition values are both variant."); } } } // Check target of "if" is the header if (Label.getBlock(IfCmp.getTarget(ifCmpInstr).target).block != header) { // No - can't optimise loop in this format // TODO: swap ifxxx around so that branch is to header and fall-through is exit throw new NonRegularLoopException("Target of exit block branch isn't the loop header."); } // Calculate stride value RegisterOperandEnumeration iteratorDefs = DefUse.defs(((RegisterOperand) carriedLoopIterator).getRegister()); // Loop over definitions of the iterator operand ignoring moves while (iteratorDefs.hasMoreElements()) { Operand curDef = follow(iteratorDefs.next()); // Is this definition within the loop? if (isInLoop(curDef.instruction.getBasicBlock())) { // Yes - have we already got an iterator instruction if ((iteratorInstr == null) || (iteratorInstr == curDef.instruction)) { // No - record iteratorInstr = curDef.instruction; } else { // Yes - loop too complex again throw new NonRegularLoopException("Multiple definitions of the iterator."); } } } // Did we find an instruction? if (iteratorInstr == null) { // No => error throw new NonRegularLoopException("No iterator definition found."); } else if ((iteratorInstr.operator.opcode != INT_ADD_opcode) && (iteratorInstr.operator.opcode != INT_SUB_opcode)) { // Is it an instruction we recognise? // TODO: support more iterator instructions throw new NonRegularLoopException("Unrecognized iterator operator " + iteratorInstr.operator); } else { // only carry on further analysis if we think we can understand the loop // Does this iterator instruction use the same register as it defines Operand iteratorUse = follow(Binary.getVal1(iteratorInstr)); // The iterator should be using a phi node of the initial and generated value if (!carriedLoopIterator.similar(iteratorUse)) { // SSA ok so far, read PHI node Instruction phiInstr = iteratorUse.instruction; if (!Phi.conforms(phiInstr)) { // We didn't find a PHI instruction throw new NonRegularLoopException("Iterator (" + iteratorUse + ") not using a phi instruction but " + phiInstr); } // We have the SSA we hoped for - tidy up strideValue = follow(Binary.getVal2(iteratorInstr)); initialIteratorValue = follow(Phi.getValue(phiInstr, 0)); phiLoopIterator = iteratorUse; if (initialIteratorValue instanceof BasicBlockOperand) { throw new Error("BasicBlock mess up!"); } if (initialIteratorValue == iteratorUse) { initialIteratorValue = follow(Phi.getValue(phiInstr, 1)); } if (initialIteratorValue instanceof BasicBlockOperand) { throw new Error("BasicBlock mess up!2"); } } else { // Not in SSA form as iterator modifies an operand throw new NonRegularLoopException("Iterator modifies (uses and defines) operand " + iteratorUse + " and is therefore not in SSA form."); } // Check the initialIteratorValue was defined outside of (before) the loop header or is constant if (!isLoopInvariant(initialIteratorValue, loop, header)) { throw new NonRegularLoopException("Initial iterator not constant or defined outside the loop - " + initialIteratorValue); } else if (!(strideValue instanceof ConstantOperand)) { // Check the stride value constant throw new NonRegularLoopException("Stride not constant - " + strideValue); } } } } } /** * Process a regular block within the loop * * @param block The basic block to process */ private void processLoopBlock(BasicBlock block) throws NonRegularLoopException { checkInEdgesAreInLoop(block); checkOutEdgesAreInLoop(block); } /** * Get the carried loop iterator * * @return carried loop iterator */ public Operand getCarriedLoopIterator() { return carriedLoopIterator; } // -oO Utility classes Oo- /** * Exception thrown when a non-regular loop is encountered */ private static class NonRegularLoopException extends Exception { /** Support for exception serialization */ static final long serialVersionUID = -7553504903633114882L; /** * Brief description of problem */ private final String _summary; /** * Constructor */ NonRegularLoopException(String s) { super(s); _summary = s; } /** * A brief description of the error */ String summary() { return _summary; } } /** * This class implements {@link BasicBlockEnumeration}. It is * used for iterating over basic blocks in a fashion determined by * the order in which basic blocks are added. */ static final class BBEnum implements BasicBlockEnumeration { /** * ArrayList holding basic blocks */ private final ArrayList<BasicBlock> blocks; /** * The current block of the iterator */ private int currentBlock; /** * Constructor */ public BBEnum() { blocks = new ArrayList<BasicBlock>(); } /** * Insert a block to the end of the list * @param block to insert */ public void add(BasicBlock block) { blocks.add(block); } /** * Is the iterator at the end of the vector * @return whether there are more elements in the vector */ public boolean hasMoreElements() { return currentBlock < blocks.size(); } /** * Get the next element from the vector and move the current block along * @return next element */ public BasicBlock nextElement() { BasicBlock result = blocks.get(currentBlock); currentBlock++; return result; } /** * Get the next element from the vector and return without requiring a cast * @return next element */ public BasicBlock next() { BasicBlock result = blocks.get(currentBlock); currentBlock++; return result; } /** * String representation of the object * @return string representing the object */ public String toString() { return blocks.toString(); } } }