/// \brief For Hexagon, if the loop label is to far from the
/// loop instruction then we need to set the LC0 and SA0 registers
/// explicitly instead of using LOOP(start,count).  This function
/// checks the distance, and generates register assignments if needed.
///
/// This function makes two passes over the basic blocks.  The first
/// pass computes the offset of the basic block from the start.
/// The second pass checks all the loop instructions.
bool HexagonFixupHwLoops::fixupLoopInstrs(MachineFunction &MF) {

  // Offset of the current instruction from the start.
  unsigned InstOffset = 0;
  // Map for each basic block to it's first instruction.
  DenseMap<const MachineBasicBlock *, unsigned> BlockToInstOffset;

  const HexagonInstrInfo *HII =
      static_cast<const HexagonInstrInfo *>(MF.getSubtarget().getInstrInfo());

  // First pass - compute the offset of each basic block.
  for (const MachineBasicBlock &MBB : MF) {
    if (MBB.getAlignment()) {
      // Although we don't know the exact layout of the final code, we need
      // to account for alignment padding somehow. This heuristic pads each
      // aligned basic block according to the alignment value.
      int ByteAlign = (1u << MBB.getAlignment()) - 1;
      InstOffset = (InstOffset + ByteAlign) & ~(ByteAlign);
    }

    BlockToInstOffset[&MBB] = InstOffset;
    for (const MachineInstr &MI : MBB)
      InstOffset += HII->getSize(MI);
  }

  // Second pass - check each loop instruction to see if it needs to be
  // converted.
  bool Changed = false;
  for (MachineBasicBlock &MBB : MF) {
    InstOffset = BlockToInstOffset[&MBB];

    // Loop over all the instructions.
    MachineBasicBlock::iterator MII = MBB.begin();
    MachineBasicBlock::iterator MIE = MBB.end();
    while (MII != MIE) {
      InstOffset += HII->getSize(*MII);
      if (MII->isMetaInstruction()) {
        ++MII;
        continue;
      }
      if (isHardwareLoop(*MII)) {
        assert(MII->getOperand(0).isMBB() &&
               "Expect a basic block as loop operand");
        int diff = InstOffset - BlockToInstOffset[MII->getOperand(0).getMBB()];
        if ((unsigned)abs(diff) > MaxLoopRange) {
          useExtLoopInstr(MF, MII);
          MII = MBB.erase(MII);
          Changed = true;
        } else {
          ++MII;
        }
      } else {
        ++MII;
      }
    }
  }

  return Changed;
}