//=- MachineLoopUtils.cpp - Functions for manipulating loops ----------------=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/MachineLoopUtils.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
using namespace llvm;

namespace {
// MI's parent and BB are clones of each other. Find the equivalent copy of MI
// in BB.
MachineInstr &findEquivalentInstruction(MachineInstr &MI,
                                        MachineBasicBlock *BB) {
  MachineBasicBlock *PB = MI.getParent();
  unsigned Offset = std::distance(PB->instr_begin(), MachineBasicBlock::instr_iterator(MI));
  return *std::next(BB->instr_begin(), Offset);
}
} // namespace

MachineBasicBlock *llvm::PeelSingleBlockLoop(LoopPeelDirection Direction,
                                             MachineBasicBlock *Loop,
                                             MachineRegisterInfo &MRI,
                                             const TargetInstrInfo *TII) {
  MachineFunction &MF = *Loop->getParent();
  MachineBasicBlock *Preheader = *Loop->pred_begin();
  if (Preheader == Loop)
    Preheader = *std::next(Loop->pred_begin());
  MachineBasicBlock *Exit = *Loop->succ_begin();
  if (Exit == Loop)
    Exit = *std::next(Loop->succ_begin());

  MachineBasicBlock *NewBB = MF.CreateMachineBasicBlock(Loop->getBasicBlock());
  if (Direction == LPD_Front)
    MF.insert(Loop->getIterator(), NewBB);
  else
    MF.insert(std::next(Loop->getIterator()), NewBB);

  DenseMap<Register, Register> Remaps;
  auto InsertPt = NewBB->end();
  for (MachineInstr &MI : *Loop) {
    MachineInstr *NewMI = MF.CloneMachineInstr(&MI);
    NewBB->insert(InsertPt, NewMI);
    for (MachineOperand &MO : NewMI->defs()) {
      Register OrigR = MO.getReg();
      if (OrigR.isPhysical())
        continue;
      Register &R = Remaps[OrigR];
      R = MRI.createVirtualRegister(MRI.getRegClass(OrigR));
      MO.setReg(R);

      if (Direction == LPD_Back) {
        // Replace all uses outside the original loop with the new register.
        // FIXME: is the use_iterator stable enough to mutate register uses
        // while iterating?
        SmallVector<MachineOperand *, 4> Uses;
        for (auto &Use : MRI.use_operands(OrigR))
          if (Use.getParent()->getParent() != Loop)
            Uses.push_back(&Use);
        for (auto *Use : Uses) {
          const TargetRegisterClass *ConstrainRegClass =
              MRI.constrainRegClass(R, MRI.getRegClass(Use->getReg()));
          assert(ConstrainRegClass &&
                 "Expected a valid constrained register class!");
          (void)ConstrainRegClass;
          Use->setReg(R);
        }
      }
    }
  }

  for (auto I = NewBB->getFirstNonPHI(); I != NewBB->end(); ++I)
    for (MachineOperand &MO : I->uses())
      if (MO.isReg())
        if (auto It = Remaps.find(MO.getReg()); It != Remaps.end())
          MO.setReg(It->second);

  for (auto I = NewBB->begin(); I->isPHI(); ++I) {
    MachineInstr &MI = *I;
    unsigned LoopRegIdx = 3, InitRegIdx = 1;
    if (MI.getOperand(2).getMBB() != Preheader)
      std::swap(LoopRegIdx, InitRegIdx);
    MachineInstr &OrigPhi = findEquivalentInstruction(MI, Loop);
    assert(OrigPhi.isPHI());
    if (Direction == LPD_Front) {
      // When peeling front, we are only left with the initial value from the
      // preheader.
      Register R = MI.getOperand(LoopRegIdx).getReg();
      if (auto It = Remaps.find(R); It != Remaps.end())
        R = It->second;
      OrigPhi.getOperand(InitRegIdx).setReg(R);
      MI.removeOperand(LoopRegIdx + 1);
      MI.removeOperand(LoopRegIdx + 0);
    } else {
      // When peeling back, the initial value is the loop-carried value from
      // the original loop.
      Register LoopReg = OrigPhi.getOperand(LoopRegIdx).getReg();
      MI.getOperand(LoopRegIdx).setReg(LoopReg);
      MI.removeOperand(InitRegIdx + 1);
      MI.removeOperand(InitRegIdx + 0);
    }
  }

  DebugLoc DL;
  if (Direction == LPD_Front) {
    Preheader->ReplaceUsesOfBlockWith(Loop, NewBB);
    NewBB->addSuccessor(Loop);
    Loop->replacePhiUsesWith(Preheader, NewBB);
    Preheader->updateTerminator(Loop);
    TII->removeBranch(*NewBB);
    TII->insertBranch(*NewBB, Loop, nullptr, {}, DL);
  } else {
    Loop->replaceSuccessor(Exit, NewBB);
    Exit->replacePhiUsesWith(Loop, NewBB);
    NewBB->addSuccessor(Exit);

    MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
    SmallVector<MachineOperand, 4> Cond;
    bool CanAnalyzeBr = !TII->analyzeBranch(*Loop, TBB, FBB, Cond);
    (void)CanAnalyzeBr;
    assert(CanAnalyzeBr && "Must be able to analyze the loop branch!");
    TII->removeBranch(*Loop);
    TII->insertBranch(*Loop, TBB == Exit ? NewBB : TBB,
                      FBB == Exit ? NewBB : FBB, Cond, DL);
    if (TII->removeBranch(*NewBB) > 0)
      TII->insertBranch(*NewBB, Exit, nullptr, {}, DL);
  }

  return NewBB;
}
