//===- BlackfinRegisterInfo.cpp - Blackfin Register Information -*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Blackfin implementation of the TargetRegisterInfo
// class.
//
//===----------------------------------------------------------------------===//

#include "Blackfin.h"
#include "BlackfinRegisterInfo.h"
#include "BlackfinSubtarget.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineLocation.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/Target/TargetFrameInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Type.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;

BlackfinRegisterInfo::BlackfinRegisterInfo(BlackfinSubtarget &st,
                                           const TargetInstrInfo &tii)
  : BlackfinGenRegisterInfo(BF::ADJCALLSTACKDOWN, BF::ADJCALLSTACKUP),
    Subtarget(st),
    TII(tii) {}

const unsigned*
BlackfinRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
  using namespace BF;
  static const unsigned CalleeSavedRegs[] = {
    FP,
    R4, R5, R6, R7,
    P3, P4, P5,
    0 };
  return  CalleeSavedRegs;
}

BitVector
BlackfinRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
  using namespace BF;
  BitVector Reserved(getNumRegs());
  Reserved.set(AZ);
  Reserved.set(AN);
  Reserved.set(AQ);
  Reserved.set(AC0);
  Reserved.set(AC1);
  Reserved.set(AV0);
  Reserved.set(AV0S);
  Reserved.set(AV1);
  Reserved.set(AV1S);
  Reserved.set(V);
  Reserved.set(VS);
  Reserved.set(CYCLES).set(CYCLES2);
  Reserved.set(L0);
  Reserved.set(L1);
  Reserved.set(L2);
  Reserved.set(L3);
  Reserved.set(SP);
  Reserved.set(RETS);
  if (hasFP(MF))
    Reserved.set(FP);
  return Reserved;
}

// hasFP - Return true if the specified function should have a dedicated frame
// pointer register.  This is true if the function has variable sized allocas or
// if frame pointer elimination is disabled.
bool BlackfinRegisterInfo::hasFP(const MachineFunction &MF) const {
  const MachineFrameInfo *MFI = MF.getFrameInfo();
  return DisableFramePointerElim(MF) ||
    MFI->adjustsStack() || MFI->hasVarSizedObjects();
}

bool BlackfinRegisterInfo::
requiresRegisterScavenging(const MachineFunction &MF) const {
  return true;
}

// Emit instructions to add delta to D/P register. ScratchReg must be of the
// same class as Reg (P).
void BlackfinRegisterInfo::adjustRegister(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator I,
                                          DebugLoc DL,
                                          unsigned Reg,
                                          unsigned ScratchReg,
                                          int delta) const {
  if (!delta)
    return;
  if (isInt<7>(delta)) {
    BuildMI(MBB, I, DL, TII.get(BF::ADDpp_imm7), Reg)
      .addReg(Reg)              // No kill on two-addr operand
      .addImm(delta);
    return;
  }

  // We must load delta into ScratchReg and add that.
  loadConstant(MBB, I, DL, ScratchReg, delta);
  if (BF::PRegClass.contains(Reg)) {
    assert(BF::PRegClass.contains(ScratchReg) &&
           "ScratchReg must be a P register");
    BuildMI(MBB, I, DL, TII.get(BF::ADDpp), Reg)
      .addReg(Reg, RegState::Kill)
      .addReg(ScratchReg, RegState::Kill);
  } else {
    assert(BF::DRegClass.contains(Reg) && "Reg must be a D or P register");
    assert(BF::DRegClass.contains(ScratchReg) &&
           "ScratchReg must be a D register");
    BuildMI(MBB, I, DL, TII.get(BF::ADD), Reg)
      .addReg(Reg, RegState::Kill)
      .addReg(ScratchReg, RegState::Kill);
  }
}

// Emit instructions to load a constant into D/P register
void BlackfinRegisterInfo::loadConstant(MachineBasicBlock &MBB,
                                        MachineBasicBlock::iterator I,
                                        DebugLoc DL,
                                        unsigned Reg,
                                        int value) const {
  if (isInt<7>(value)) {
    BuildMI(MBB, I, DL, TII.get(BF::LOADimm7), Reg).addImm(value);
    return;
  }

  if (isUInt<16>(value)) {
    BuildMI(MBB, I, DL, TII.get(BF::LOADuimm16), Reg).addImm(value);
    return;
  }

  if (isInt<16>(value)) {
    BuildMI(MBB, I, DL, TII.get(BF::LOADimm16), Reg).addImm(value);
    return;
  }

  // We must split into halves
  BuildMI(MBB, I, DL,
          TII.get(BF::LOAD16i), getSubReg(Reg, BF::hi16))
    .addImm((value >> 16) & 0xffff)
    .addReg(Reg, RegState::ImplicitDefine);
  BuildMI(MBB, I, DL,
          TII.get(BF::LOAD16i), getSubReg(Reg, BF::lo16))
    .addImm(value & 0xffff)
    .addReg(Reg, RegState::ImplicitKill)
    .addReg(Reg, RegState::ImplicitDefine);
}

void BlackfinRegisterInfo::
eliminateCallFramePseudoInstr(MachineFunction &MF,
                              MachineBasicBlock &MBB,
                              MachineBasicBlock::iterator I) const {
  if (!hasReservedCallFrame(MF)) {
    int64_t Amount = I->getOperand(0).getImm();
    if (Amount != 0) {
      assert(Amount%4 == 0 && "Unaligned call frame size");
      if (I->getOpcode() == BF::ADJCALLSTACKDOWN) {
        adjustRegister(MBB, I, I->getDebugLoc(), BF::SP, BF::P1, -Amount);
      } else {
        assert(I->getOpcode() == BF::ADJCALLSTACKUP &&
               "Unknown call frame pseudo instruction");
        adjustRegister(MBB, I, I->getDebugLoc(), BF::SP, BF::P1, Amount);
      }
    }
  }
  MBB.erase(I);
}

/// findScratchRegister - Find a 'free' register. Try for a call-clobbered
/// register first and then a spilled callee-saved register if that fails.
static unsigned findScratchRegister(MachineBasicBlock::iterator II,
                                    RegScavenger *RS,
                                    const TargetRegisterClass *RC,
                                    int SPAdj) {
  assert(RS && "Register scavenging must be on");
  unsigned Reg = RS->FindUnusedReg(RC);
  if (Reg == 0)
    Reg = RS->scavengeRegister(RC, II, SPAdj);
  return Reg;
}

unsigned
BlackfinRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
                                          int SPAdj, FrameIndexValue *Value,
                                          RegScavenger *RS) const {
  MachineInstr &MI = *II;
  MachineBasicBlock &MBB = *MI.getParent();
  MachineFunction &MF = *MBB.getParent();
  DebugLoc DL = MI.getDebugLoc();

  unsigned FIPos;
  for (FIPos=0; !MI.getOperand(FIPos).isFI(); ++FIPos) {
    assert(FIPos < MI.getNumOperands() &&
           "Instr doesn't have FrameIndex operand!");
  }
  int FrameIndex = MI.getOperand(FIPos).getIndex();
  assert(FIPos+1 < MI.getNumOperands() && MI.getOperand(FIPos+1).isImm());
  int Offset = MF.getFrameInfo()->getObjectOffset(FrameIndex)
    + MI.getOperand(FIPos+1).getImm();
  unsigned BaseReg = BF::FP;
  if (hasFP(MF)) {
    assert(SPAdj==0 && "Unexpected SP adjust in function with frame pointer");
  } else {
    BaseReg = BF::SP;
    Offset += MF.getFrameInfo()->getStackSize() + SPAdj;
  }

  bool isStore = false;

  switch (MI.getOpcode()) {
  case BF::STORE32fi:
    isStore = true;
  case BF::LOAD32fi: {
    assert(Offset%4 == 0 && "Unaligned i32 stack access");
    assert(FIPos==1 && "Bad frame index operand");
    MI.getOperand(FIPos).ChangeToRegister(BaseReg, false);
    MI.getOperand(FIPos+1).setImm(Offset);
    if (isUInt<6>(Offset)) {
      MI.setDesc(TII.get(isStore
                         ? BF::STORE32p_uimm6m4
                         : BF::LOAD32p_uimm6m4));
      return 0;
    }
    if (BaseReg == BF::FP && isUInt<7>(-Offset)) {
      MI.setDesc(TII.get(isStore
                         ? BF::STORE32fp_nimm7m4
                         : BF::LOAD32fp_nimm7m4));
      MI.getOperand(FIPos+1).setImm(-Offset);
      return 0;
    }
    if (isInt<18>(Offset)) {
      MI.setDesc(TII.get(isStore
                         ? BF::STORE32p_imm18m4
                         : BF::LOAD32p_imm18m4));
      return 0;
    }
    // Use RegScavenger to calculate proper offset...
    MI.dump();
    llvm_unreachable("Stack frame offset too big");
    break;
  }
  case BF::ADDpp: {
    assert(MI.getOperand(0).isReg() && "ADD instruction needs a register");
    unsigned DestReg = MI.getOperand(0).getReg();
    // We need to produce a stack offset in a P register. We emit:
    // P0 = offset;
    // P0 = BR + P0;
    assert(FIPos==1 && "Bad frame index operand");
    loadConstant(MBB, II, DL, DestReg, Offset);
    MI.getOperand(1).ChangeToRegister(DestReg, false, false, true);
    MI.getOperand(2).ChangeToRegister(BaseReg, false);
    break;
  }
  case BF::STORE16fi:
    isStore = true;
  case BF::LOAD16fi: {
    assert(Offset%2 == 0 && "Unaligned i16 stack access");
    assert(FIPos==1 && "Bad frame index operand");
    // We need a P register to use as an address
    unsigned ScratchReg = findScratchRegister(II, RS, &BF::PRegClass, SPAdj);
    assert(ScratchReg && "Could not scavenge register");
    loadConstant(MBB, II, DL, ScratchReg, Offset);
    BuildMI(MBB, II, DL, TII.get(BF::ADDpp), ScratchReg)
      .addReg(ScratchReg, RegState::Kill)
      .addReg(BaseReg);
    MI.setDesc(TII.get(isStore ? BF::STORE16pi : BF::LOAD16pi));
    MI.getOperand(1).ChangeToRegister(ScratchReg, false, false, true);
    MI.RemoveOperand(2);
    break;
  }
  case BF::STORE8fi: {
    // This is an AnyCC spill, we need a scratch register.
    assert(FIPos==1 && "Bad frame index operand");
    MachineOperand SpillReg = MI.getOperand(0);
    unsigned ScratchReg = findScratchRegister(II, RS, &BF::DRegClass, SPAdj);
    assert(ScratchReg && "Could not scavenge register");
    if (SpillReg.getReg()==BF::NCC) {
      BuildMI(MBB, II, DL, TII.get(BF::MOVENCC_z), ScratchReg)
        .addOperand(SpillReg);
      BuildMI(MBB, II, DL, TII.get(BF::BITTGL), ScratchReg)
        .addReg(ScratchReg).addImm(0);
    } else {
      BuildMI(MBB, II, DL, TII.get(BF::MOVECC_zext), ScratchReg)
        .addOperand(SpillReg);
    }
    // STORE D
    MI.setDesc(TII.get(BF::STORE8p_imm16));
    MI.getOperand(0).ChangeToRegister(ScratchReg, false, false, true);
    MI.getOperand(FIPos).ChangeToRegister(BaseReg, false);
    MI.getOperand(FIPos+1).setImm(Offset);
    break;
  }
  case BF::LOAD8fi: {
    // This is an restore, we need a scratch register.
    assert(FIPos==1 && "Bad frame index operand");
    MachineOperand SpillReg = MI.getOperand(0);
    unsigned ScratchReg = findScratchRegister(II, RS, &BF::DRegClass, SPAdj);
    assert(ScratchReg && "Could not scavenge register");
    MI.setDesc(TII.get(BF::LOAD32p_imm16_8z));
    MI.getOperand(0).ChangeToRegister(ScratchReg, true);
    MI.getOperand(FIPos).ChangeToRegister(BaseReg, false);
    MI.getOperand(FIPos+1).setImm(Offset);
    ++II;
    if (SpillReg.getReg()==BF::CC) {
      // CC = D
      BuildMI(MBB, II, DL, TII.get(BF::MOVECC_nz), BF::CC)
        .addReg(ScratchReg, RegState::Kill);
    } else {
      // Restore NCC (CC = D==0)
      BuildMI(MBB, II, DL, TII.get(BF::SETEQri_not), BF::NCC)
        .addReg(ScratchReg, RegState::Kill)
        .addImm(0);
    }
    break;
  }
  default:
    llvm_unreachable("Cannot eliminate frame index");
    break;
  }
  return 0;
}

void BlackfinRegisterInfo::
processFunctionBeforeCalleeSavedScan(MachineFunction &MF,
                                     RegScavenger *RS) const {
  MachineFrameInfo *MFI = MF.getFrameInfo();
  const TargetRegisterClass *RC = BF::DPRegisterClass;
  if (requiresRegisterScavenging(MF)) {
    // Reserve a slot close to SP or frame pointer.
    RS->setScavengingFrameIndex(MFI->CreateStackObject(RC->getSize(),
                                                       RC->getAlignment(),
                                                       false));
  }
}

void BlackfinRegisterInfo::
processFunctionBeforeFrameFinalized(MachineFunction &MF) const {
}

// Emit a prologue that sets up a stack frame.
// On function entry, R0-R2 and P0 may hold arguments.
// R3, P1, and P2 may be used as scratch registers
void BlackfinRegisterInfo::emitPrologue(MachineFunction &MF) const {
  MachineBasicBlock &MBB = MF.front();   // Prolog goes in entry BB
  MachineBasicBlock::iterator MBBI = MBB.begin();
  MachineFrameInfo *MFI = MF.getFrameInfo();
  DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();

  int FrameSize = MFI->getStackSize();
  if (FrameSize%4) {
    FrameSize = (FrameSize+3) & ~3;
    MFI->setStackSize(FrameSize);
  }

  if (!hasFP(MF)) {
    assert(!MFI->adjustsStack() &&
           "FP elimination on a non-leaf function is not supported");
    adjustRegister(MBB, MBBI, dl, BF::SP, BF::P1, -FrameSize);
    return;
  }

  // emit a LINK instruction
  if (FrameSize <= 0x3ffff) {
    BuildMI(MBB, MBBI, dl, TII.get(BF::LINK)).addImm(FrameSize);
    return;
  }

  // Frame is too big, do a manual LINK:
  // [--SP] = RETS;
  // [--SP] = FP;
  // FP = SP;
  // P1 = -FrameSize;
  // SP = SP + P1;
  BuildMI(MBB, MBBI, dl, TII.get(BF::PUSH))
    .addReg(BF::RETS, RegState::Kill);
  BuildMI(MBB, MBBI, dl, TII.get(BF::PUSH))
    .addReg(BF::FP, RegState::Kill);
  BuildMI(MBB, MBBI, dl, TII.get(BF::MOVE), BF::FP)
    .addReg(BF::SP);
  loadConstant(MBB, MBBI, dl, BF::P1, -FrameSize);
  BuildMI(MBB, MBBI, dl, TII.get(BF::ADDpp), BF::SP)
    .addReg(BF::SP, RegState::Kill)
    .addReg(BF::P1, RegState::Kill);

}

void BlackfinRegisterInfo::emitEpilogue(MachineFunction &MF,
                                        MachineBasicBlock &MBB) const {
  MachineFrameInfo *MFI = MF.getFrameInfo();
  MachineBasicBlock::iterator MBBI = prior(MBB.end());
  DebugLoc dl = MBBI->getDebugLoc();

  int FrameSize = MFI->getStackSize();
  assert(FrameSize%4 == 0 && "Misaligned frame size");

  if (!hasFP(MF)) {
    assert(!MFI->adjustsStack() &&
           "FP elimination on a non-leaf function is not supported");
    adjustRegister(MBB, MBBI, dl, BF::SP, BF::P1, FrameSize);
    return;
  }

  // emit an UNLINK instruction
  BuildMI(MBB, MBBI, dl, TII.get(BF::UNLINK));
}

unsigned BlackfinRegisterInfo::getRARegister() const {
  return BF::RETS;
}

unsigned
BlackfinRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
  return hasFP(MF) ? BF::FP : BF::SP;
}

unsigned BlackfinRegisterInfo::getEHExceptionRegister() const {
  llvm_unreachable("What is the exception register");
  return 0;
}

unsigned BlackfinRegisterInfo::getEHHandlerRegister() const {
  llvm_unreachable("What is the exception handler register");
  return 0;
}

int BlackfinRegisterInfo::getDwarfRegNum(unsigned RegNum, bool isEH) const {
  llvm_unreachable("What is the dwarf register number");
  return -1;
}

#include "BlackfinGenRegisterInfo.inc"

