check_exit_block_exits:
  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    OPT_BasicBlockEnumeration exitBlock_outEdges = exit.getOut();    boolean exits = false;    check_exit_block_exits:    while(exitBlock_outEdges.hasMoreElements()){      OPT_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 OPT_LoopUnrolling.java : 240");        }      }    } // end of check_exit_block_exits    if (exits == false){      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 = (OPT_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              OPT_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        OPT_RegisterOperandEnumeration iteratorDefs = OPT_DefUse.defs (((OPT_RegisterOperand)carriedLoopIterator).register);        // Loop over definitions of the iterator operand ignoring moves        while (iteratorDefs.hasMoreElements()) {         OPT_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.");        }        // Is it an instruction we recognise?        // TODO: support more iterator instructions        else if ((iteratorInstr.operator.opcode != INT_ADD_opcode)&&(iteratorInstr.operator.opcode != INT_SUB_opcode)){          throw new NonRegularLoopException("Unrecognized iterator operator " + iteratorInstr.operator);        }        // only carry on further analysis if we think we can understand the loop        else {          // Does this iterator instruction use the same register as it defines          OPT_Operand iteratorUse = follow(Binary.getVal1(iteratorInstr));          // The iterator should be using a phi node of the initial and generated value          if (((OPT_RegisterOperand)carriedLoopIterator).similar(iteratorUse) == false){            // SSA ok so far, read PHI node            OPT_Instruction phiInstr = iteratorUse.instruction;            if (Phi.conforms(phiInstr) == false) {              // 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 OPT_BasicBlockOperand) {              throw new Error("OPT_BasicBlock mess up!");            }            if (initialIteratorValue == iteratorUse) {              initialIteratorValue = follow(Phi.getValue(phiInstr,1));            }            if (initialIteratorValue instanceof OPT_BasicBlockOperand) {              throw new Error("OPT_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) == false) {            throw new NonRegularLoopException("Initial iterator not constant or defined outside the loop - " + initialIteratorValue);          }          // Check the stride value constant          else if ((strideValue instanceof OPT_ConstantOperand) == false){            throw new NonRegularLoopException("Stride not constant - " + strideValue);          }        }      }    }  }