//===- AArch64InstrInfo.cpp - AArch64 Instruction Information -------------===//
//
//                     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 AArch64 implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//

#include "AArch64.h"
#include "AArch64InstrInfo.h"
#include "AArch64MachineFunctionInfo.h"
#include "AArch64TargetMachine.h"
#include "MCTargetDesc/AArch64MCTargetDesc.h"
#include "Utils/AArch64BaseInfo.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TargetRegistry.h"

#include <algorithm>

#define GET_INSTRINFO_CTOR
#include "AArch64GenInstrInfo.inc"

using namespace llvm;

AArch64InstrInfo::AArch64InstrInfo(const AArch64Subtarget &STI)
  : AArch64GenInstrInfo(AArch64::ADJCALLSTACKDOWN, AArch64::ADJCALLSTACKUP),
    Subtarget(STI) {}

void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
                                   MachineBasicBlock::iterator I, DebugLoc DL,
                                   unsigned DestReg, unsigned SrcReg,
                                   bool KillSrc) const {
  unsigned Opc = 0;
  unsigned ZeroReg = 0;
  if (DestReg == AArch64::XSP || SrcReg == AArch64::XSP) {
    // E.g. ADD xDst, xsp, #0 (, lsl #0)
    BuildMI(MBB, I, DL, get(AArch64::ADDxxi_lsl0_s), DestReg)
      .addReg(SrcReg)
      .addImm(0);
    return;
  } else if (DestReg == AArch64::WSP || SrcReg == AArch64::WSP) {
    // E.g. ADD wDST, wsp, #0 (, lsl #0)
    BuildMI(MBB, I, DL, get(AArch64::ADDwwi_lsl0_s), DestReg)
      .addReg(SrcReg)
      .addImm(0);
    return;
  } else if (DestReg == AArch64::NZCV) {
    assert(AArch64::GPR64RegClass.contains(SrcReg));
    // E.g. MSR NZCV, xDST
    BuildMI(MBB, I, DL, get(AArch64::MSRix))
      .addImm(A64SysReg::NZCV)
      .addReg(SrcReg);
  } else if (SrcReg == AArch64::NZCV) {
    assert(AArch64::GPR64RegClass.contains(DestReg));
    // E.g. MRS xDST, NZCV
    BuildMI(MBB, I, DL, get(AArch64::MRSxi), DestReg)
      .addImm(A64SysReg::NZCV);
  } else if (AArch64::GPR64RegClass.contains(DestReg)) {
    if(AArch64::GPR64RegClass.contains(SrcReg)){
      Opc = AArch64::ORRxxx_lsl;
      ZeroReg = AArch64::XZR;
    } else{
      assert(AArch64::FPR64RegClass.contains(SrcReg));
      BuildMI(MBB, I, DL, get(AArch64::FMOVxd), DestReg)
        .addReg(SrcReg);
      return;
    }
  } else if (AArch64::GPR32RegClass.contains(DestReg)) {
    if(AArch64::GPR32RegClass.contains(SrcReg)){
      Opc = AArch64::ORRwww_lsl;
      ZeroReg = AArch64::WZR;
    } else{
      assert(AArch64::FPR32RegClass.contains(SrcReg));
      BuildMI(MBB, I, DL, get(AArch64::FMOVws), DestReg)
        .addReg(SrcReg);
      return;
    }
  } else if (AArch64::FPR32RegClass.contains(DestReg)) {
    if(AArch64::FPR32RegClass.contains(SrcReg)){
      BuildMI(MBB, I, DL, get(AArch64::FMOVss), DestReg)
        .addReg(SrcReg);
      return;
    }
    else {
      assert(AArch64::GPR32RegClass.contains(SrcReg));
      BuildMI(MBB, I, DL, get(AArch64::FMOVsw), DestReg)
        .addReg(SrcReg);
      return;
    }
  } else if (AArch64::FPR64RegClass.contains(DestReg)) {
    if(AArch64::FPR64RegClass.contains(SrcReg)){
      BuildMI(MBB, I, DL, get(AArch64::FMOVdd), DestReg)
        .addReg(SrcReg);
      return;
    }
    else {
      assert(AArch64::GPR64RegClass.contains(SrcReg));
      BuildMI(MBB, I, DL, get(AArch64::FMOVdx), DestReg)
        .addReg(SrcReg);
      return;
    }
  } else if (AArch64::FPR128RegClass.contains(DestReg)) {
    assert(AArch64::FPR128RegClass.contains(SrcReg));

    // FIXME: there's no good way to do this, at least without NEON:
    //   + There's no single move instruction for q-registers
    //   + We can't create a spill slot and use normal STR/LDR because stack
    //     allocation has already happened
    //   + We can't go via X-registers with FMOV because register allocation has
    //     already happened.
    // This may not be efficient, but at least it works.
    BuildMI(MBB, I, DL, get(AArch64::LSFP128_PreInd_STR), AArch64::XSP)
      .addReg(SrcReg)
      .addReg(AArch64::XSP)
      .addImm(0x1ff & -16);

    BuildMI(MBB, I, DL, get(AArch64::LSFP128_PostInd_LDR), DestReg)
      .addReg(AArch64::XSP, RegState::Define)
      .addReg(AArch64::XSP)
      .addImm(16);
    return;
  } else {
    llvm_unreachable("Unknown register class in copyPhysReg");
  }

  // E.g. ORR xDst, xzr, xSrc, lsl #0
  BuildMI(MBB, I, DL, get(Opc), DestReg)
    .addReg(ZeroReg)
    .addReg(SrcReg)
    .addImm(0);
}

/// Does the Opcode represent a conditional branch that we can remove and re-add
/// at the end of a basic block?
static bool isCondBranch(unsigned Opc) {
  return Opc == AArch64::Bcc || Opc == AArch64::CBZw || Opc == AArch64::CBZx ||
         Opc == AArch64::CBNZw || Opc == AArch64::CBNZx ||
         Opc == AArch64::TBZwii || Opc == AArch64::TBZxii ||
         Opc == AArch64::TBNZwii || Opc == AArch64::TBNZxii;
}

/// Takes apart a given conditional branch MachineInstr (see isCondBranch),
/// setting TBB to the destination basic block and populating the Cond vector
/// with data necessary to recreate the conditional branch at a later
/// date. First element will be the opcode, and subsequent ones define the
/// conditions being branched on in an instruction-specific manner.
static void classifyCondBranch(MachineInstr *I, MachineBasicBlock *&TBB,
                               SmallVectorImpl<MachineOperand> &Cond) {
  switch(I->getOpcode()) {
  case AArch64::Bcc:
  case AArch64::CBZw:
  case AArch64::CBZx:
  case AArch64::CBNZw:
  case AArch64::CBNZx:
    // These instructions just have one predicate operand in position 0 (either
    // a condition code or a register being compared).
    Cond.push_back(MachineOperand::CreateImm(I->getOpcode()));
    Cond.push_back(I->getOperand(0));
    TBB = I->getOperand(1).getMBB();
    return;
  case AArch64::TBZwii:
  case AArch64::TBZxii:
  case AArch64::TBNZwii:
  case AArch64::TBNZxii:
    // These have two predicate operands: a register and a bit position.
    Cond.push_back(MachineOperand::CreateImm(I->getOpcode()));
    Cond.push_back(I->getOperand(0));
    Cond.push_back(I->getOperand(1));
    TBB = I->getOperand(2).getMBB();
    return;
  default:
    llvm_unreachable("Unknown conditional branch to classify");
  }
}


bool
AArch64InstrInfo::AnalyzeBranch(MachineBasicBlock &MBB,MachineBasicBlock *&TBB,
                                MachineBasicBlock *&FBB,
                                SmallVectorImpl<MachineOperand> &Cond,
                                bool AllowModify) const {
  // If the block has no terminators, it just falls into the block after it.
  MachineBasicBlock::iterator I = MBB.end();
  if (I == MBB.begin())
    return false;
  --I;
  while (I->isDebugValue()) {
    if (I == MBB.begin())
      return false;
    --I;
  }
  if (!isUnpredicatedTerminator(I))
    return false;

  // Get the last instruction in the block.
  MachineInstr *LastInst = I;

  // If there is only one terminator instruction, process it.
  unsigned LastOpc = LastInst->getOpcode();
  if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) {
    if (LastOpc == AArch64::Bimm) {
      TBB = LastInst->getOperand(0).getMBB();
      return false;
    }
    if (isCondBranch(LastOpc)) {
      classifyCondBranch(LastInst, TBB, Cond);
      return false;
    }
    return true;  // Can't handle indirect branch.
  }

  // Get the instruction before it if it is a terminator.
  MachineInstr *SecondLastInst = I;
  unsigned SecondLastOpc = SecondLastInst->getOpcode();

  // If AllowModify is true and the block ends with two or more unconditional
  // branches, delete all but the first unconditional branch.
  if (AllowModify && LastOpc == AArch64::Bimm) {
    while (SecondLastOpc == AArch64::Bimm) {
      LastInst->eraseFromParent();
      LastInst = SecondLastInst;
      LastOpc = LastInst->getOpcode();
      if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) {
        // Return now the only terminator is an unconditional branch.
        TBB = LastInst->getOperand(0).getMBB();
        return false;
      } else {
        SecondLastInst = I;
        SecondLastOpc = SecondLastInst->getOpcode();
      }
    }
  }

  // If there are three terminators, we don't know what sort of block this is.
  if (SecondLastInst && I != MBB.begin() && isUnpredicatedTerminator(--I))
    return true;

  // If the block ends with a B and a Bcc, handle it.
  if (LastOpc == AArch64::Bimm) {
    if (SecondLastOpc == AArch64::Bcc) {
      TBB =  SecondLastInst->getOperand(1).getMBB();
      Cond.push_back(MachineOperand::CreateImm(AArch64::Bcc));
      Cond.push_back(SecondLastInst->getOperand(0));
      FBB = LastInst->getOperand(0).getMBB();
      return false;
    } else if (isCondBranch(SecondLastOpc)) {
      classifyCondBranch(SecondLastInst, TBB, Cond);
      FBB = LastInst->getOperand(0).getMBB();
      return false;
    }
  }

  // If the block ends with two unconditional branches, handle it.  The second
  // one is not executed, so remove it.
  if (SecondLastOpc == AArch64::Bimm && LastOpc == AArch64::Bimm) {
    TBB = SecondLastInst->getOperand(0).getMBB();
    I = LastInst;
    if (AllowModify)
      I->eraseFromParent();
    return false;
  }

  // Otherwise, can't handle this.
  return true;
}

bool AArch64InstrInfo::ReverseBranchCondition(
                                  SmallVectorImpl<MachineOperand> &Cond) const {
  switch (Cond[0].getImm()) {
  case AArch64::Bcc: {
    A64CC::CondCodes CC = static_cast<A64CC::CondCodes>(Cond[1].getImm());
    CC = A64InvertCondCode(CC);
    Cond[1].setImm(CC);
    return false;
  }
  case AArch64::CBZw:
    Cond[0].setImm(AArch64::CBNZw);
    return false;
  case AArch64::CBZx:
    Cond[0].setImm(AArch64::CBNZx);
    return false;
  case AArch64::CBNZw:
    Cond[0].setImm(AArch64::CBZw);
    return false;
  case AArch64::CBNZx:
    Cond[0].setImm(AArch64::CBZx);
    return false;
  case AArch64::TBZwii:
    Cond[0].setImm(AArch64::TBNZwii);
    return false;
  case AArch64::TBZxii:
    Cond[0].setImm(AArch64::TBNZxii);
    return false;
  case AArch64::TBNZwii:
    Cond[0].setImm(AArch64::TBZwii);
    return false;
  case AArch64::TBNZxii:
    Cond[0].setImm(AArch64::TBZxii);
    return false;
  default:
    llvm_unreachable("Unknown branch type");
  }
}


unsigned
AArch64InstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
                               MachineBasicBlock *FBB,
                               const SmallVectorImpl<MachineOperand> &Cond,
                               DebugLoc DL) const {
  if (FBB == 0 && Cond.empty()) {
    BuildMI(&MBB, DL, get(AArch64::Bimm)).addMBB(TBB);
    return 1;
  } else if (FBB == 0) {
    MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm()));
    for (int i = 1, e = Cond.size(); i != e; ++i)
      MIB.addOperand(Cond[i]);
    MIB.addMBB(TBB);
    return 1;
  }

  MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm()));
  for (int i = 1, e = Cond.size(); i != e; ++i)
    MIB.addOperand(Cond[i]);
  MIB.addMBB(TBB);

  BuildMI(&MBB, DL, get(AArch64::Bimm)).addMBB(FBB);
  return 2;
}

unsigned AArch64InstrInfo::RemoveBranch(MachineBasicBlock &MBB) const {
  MachineBasicBlock::iterator I = MBB.end();
  if (I == MBB.begin()) return 0;
  --I;
  while (I->isDebugValue()) {
    if (I == MBB.begin())
      return 0;
    --I;
  }
  if (I->getOpcode() != AArch64::Bimm && !isCondBranch(I->getOpcode()))
    return 0;

  // Remove the branch.
  I->eraseFromParent();

  I = MBB.end();

  if (I == MBB.begin()) return 1;
  --I;
  if (!isCondBranch(I->getOpcode()))
    return 1;

  // Remove the branch.
  I->eraseFromParent();
  return 2;
}

bool
AArch64InstrInfo::expandPostRAPseudo(MachineBasicBlock::iterator MBBI) const {
  MachineInstr &MI = *MBBI;
  MachineBasicBlock &MBB = *MI.getParent();

  unsigned Opcode = MI.getOpcode();
  switch (Opcode) {
  case AArch64::TLSDESC_BLRx: {
    MachineInstr *NewMI =
      BuildMI(MBB, MBBI, MI.getDebugLoc(), get(AArch64::TLSDESCCALL))
        .addOperand(MI.getOperand(1));
    MI.setDesc(get(AArch64::BLRx));

    llvm::finalizeBundle(MBB, NewMI, *++MBBI);
    return true;
    }
  default:
    return false;
  }

  return false;
}

void
AArch64InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
                                      MachineBasicBlock::iterator MBBI,
                                      unsigned SrcReg, bool isKill,
                                      int FrameIdx,
                                      const TargetRegisterClass *RC,
                                      const TargetRegisterInfo *TRI) const {
  DebugLoc DL = MBB.findDebugLoc(MBBI);
  MachineFunction &MF = *MBB.getParent();
  MachineFrameInfo &MFI = *MF.getFrameInfo();
  unsigned Align = MFI.getObjectAlignment(FrameIdx);

  MachineMemOperand *MMO
    = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx),
                              MachineMemOperand::MOStore,
                              MFI.getObjectSize(FrameIdx),
                              Align);

  unsigned StoreOp = 0;
  if (RC->hasType(MVT::i64) || RC->hasType(MVT::i32)) {
    switch(RC->getSize()) {
    case 4: StoreOp = AArch64::LS32_STR; break;
    case 8: StoreOp = AArch64::LS64_STR; break;
    default:
      llvm_unreachable("Unknown size for regclass");
    }
  } else {
    assert((RC->hasType(MVT::f32) || RC->hasType(MVT::f64) ||
            RC->hasType(MVT::f128))
           && "Expected integer or floating type for store");
    switch (RC->getSize()) {
    case 4: StoreOp = AArch64::LSFP32_STR; break;
    case 8: StoreOp = AArch64::LSFP64_STR; break;
    case 16: StoreOp = AArch64::LSFP128_STR; break;
    default:
      llvm_unreachable("Unknown size for regclass");
    }
  }

  MachineInstrBuilder NewMI = BuildMI(MBB, MBBI, DL, get(StoreOp));
  NewMI.addReg(SrcReg, getKillRegState(isKill))
    .addFrameIndex(FrameIdx)
    .addImm(0)
    .addMemOperand(MMO);

}

void
AArch64InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
                                       MachineBasicBlock::iterator MBBI,
                                       unsigned DestReg, int FrameIdx,
                                       const TargetRegisterClass *RC,
                                       const TargetRegisterInfo *TRI) const {
  DebugLoc DL = MBB.findDebugLoc(MBBI);
  MachineFunction &MF = *MBB.getParent();
  MachineFrameInfo &MFI = *MF.getFrameInfo();
  unsigned Align = MFI.getObjectAlignment(FrameIdx);

  MachineMemOperand *MMO
    = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx),
                              MachineMemOperand::MOLoad,
                              MFI.getObjectSize(FrameIdx),
                              Align);

  unsigned LoadOp = 0;
  if (RC->hasType(MVT::i64) || RC->hasType(MVT::i32)) {
    switch(RC->getSize()) {
    case 4: LoadOp = AArch64::LS32_LDR; break;
    case 8: LoadOp = AArch64::LS64_LDR; break;
    default:
      llvm_unreachable("Unknown size for regclass");
    }
  } else {
    assert((RC->hasType(MVT::f32) || RC->hasType(MVT::f64)
            || RC->hasType(MVT::f128))
           && "Expected integer or floating type for store");
    switch (RC->getSize()) {
    case 4: LoadOp = AArch64::LSFP32_LDR; break;
    case 8: LoadOp = AArch64::LSFP64_LDR; break;
    case 16: LoadOp = AArch64::LSFP128_LDR; break;
    default:
      llvm_unreachable("Unknown size for regclass");
    }
  }

  MachineInstrBuilder NewMI = BuildMI(MBB, MBBI, DL, get(LoadOp), DestReg);
  NewMI.addFrameIndex(FrameIdx)
       .addImm(0)
       .addMemOperand(MMO);
}

unsigned AArch64InstrInfo::estimateRSStackLimit(MachineFunction &MF) const {
  unsigned Limit = (1 << 16) - 1;
  for (MachineFunction::iterator BB = MF.begin(),E = MF.end(); BB != E; ++BB) {
    for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end();
         I != E; ++I) {
      for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) {
        if (!I->getOperand(i).isFI()) continue;

        // When using ADDxxi_lsl0_s to get the address of a stack object, 0xfff
        // is the largest offset guaranteed to fit in the immediate offset.
        if (I->getOpcode() == AArch64::ADDxxi_lsl0_s) {
          Limit = std::min(Limit, 0xfffu);
          break;
        }

        int AccessScale, MinOffset, MaxOffset;
        getAddressConstraints(*I, AccessScale, MinOffset, MaxOffset);
        Limit = std::min(Limit, static_cast<unsigned>(MaxOffset));

        break; // At most one FI per instruction
      }
    }
  }

  return Limit;
}
void AArch64InstrInfo::getAddressConstraints(const MachineInstr &MI,
                                             int &AccessScale, int &MinOffset,
                                             int &MaxOffset) const {
  switch (MI.getOpcode()) {
  default: llvm_unreachable("Unkown load/store kind");
  case TargetOpcode::DBG_VALUE:
    AccessScale = 1;
    MinOffset = INT_MIN;
    MaxOffset = INT_MAX;
    return;
  case AArch64::LS8_LDR: case AArch64::LS8_STR:
  case AArch64::LSFP8_LDR: case AArch64::LSFP8_STR:
  case AArch64::LDRSBw:
  case AArch64::LDRSBx:
    AccessScale = 1;
    MinOffset = 0;
    MaxOffset = 0xfff;
    return;
  case AArch64::LS16_LDR: case AArch64::LS16_STR:
  case AArch64::LSFP16_LDR: case AArch64::LSFP16_STR:
  case AArch64::LDRSHw:
  case AArch64::LDRSHx:
    AccessScale = 2;
    MinOffset = 0;
    MaxOffset = 0xfff * AccessScale;
    return;
  case AArch64::LS32_LDR:  case AArch64::LS32_STR:
  case AArch64::LSFP32_LDR: case AArch64::LSFP32_STR:
  case AArch64::LDRSWx:
  case AArch64::LDPSWx:
    AccessScale = 4;
    MinOffset = 0;
    MaxOffset = 0xfff * AccessScale;
    return;
  case AArch64::LS64_LDR: case AArch64::LS64_STR:
  case AArch64::LSFP64_LDR: case AArch64::LSFP64_STR:
  case AArch64::PRFM:
    AccessScale = 8;
    MinOffset = 0;
    MaxOffset = 0xfff * AccessScale;
    return;
  case AArch64::LSFP128_LDR: case AArch64::LSFP128_STR:
    AccessScale = 16;
    MinOffset = 0;
    MaxOffset = 0xfff * AccessScale;
    return;
  case AArch64::LSPair32_LDR: case AArch64::LSPair32_STR:
  case AArch64::LSFPPair32_LDR: case AArch64::LSFPPair32_STR:
    AccessScale = 4;
    MinOffset = -0x40 * AccessScale;
    MaxOffset = 0x3f * AccessScale;
    return;
  case AArch64::LSPair64_LDR: case AArch64::LSPair64_STR:
  case AArch64::LSFPPair64_LDR: case AArch64::LSFPPair64_STR:
    AccessScale = 8;
    MinOffset = -0x40 * AccessScale;
    MaxOffset = 0x3f * AccessScale;
    return;
  case AArch64::LSFPPair128_LDR: case AArch64::LSFPPair128_STR:
    AccessScale = 16;
    MinOffset = -0x40 * AccessScale;
    MaxOffset = 0x3f * AccessScale;
    return;
  }
}

unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
  const MCInstrDesc &MCID = MI.getDesc();
  const MachineBasicBlock &MBB = *MI.getParent();
  const MachineFunction &MF = *MBB.getParent();
  const MCAsmInfo &MAI = *MF.getTarget().getMCAsmInfo();

  if (MCID.getSize())
    return MCID.getSize();

  if (MI.getOpcode() == AArch64::INLINEASM)
    return getInlineAsmLength(MI.getOperand(0).getSymbolName(), MAI);

  if (MI.isLabel())
    return 0;

  switch (MI.getOpcode()) {
  case TargetOpcode::BUNDLE:
    return getInstBundleLength(MI);
  case TargetOpcode::IMPLICIT_DEF:
  case TargetOpcode::KILL:
  case TargetOpcode::PROLOG_LABEL:
  case TargetOpcode::EH_LABEL:
  case TargetOpcode::DBG_VALUE:
    return 0;
  case AArch64::TLSDESCCALL:
    return 0;
  default:
    llvm_unreachable("Unknown instruction class");
  }
}

unsigned AArch64InstrInfo::getInstBundleLength(const MachineInstr &MI) const {
  unsigned Size = 0;
  MachineBasicBlock::const_instr_iterator I = MI;
  MachineBasicBlock::const_instr_iterator E = MI.getParent()->instr_end();
  while (++I != E && I->isInsideBundle()) {
    assert(!I->isBundle() && "No nested bundle!");
    Size += getInstSizeInBytes(*I);
  }
  return Size;
}

bool llvm::rewriteA64FrameIndex(MachineInstr &MI, unsigned FrameRegIdx,
                                unsigned FrameReg, int &Offset,
                                const AArch64InstrInfo &TII) {
  MachineBasicBlock &MBB = *MI.getParent();
  MachineFunction &MF = *MBB.getParent();
  MachineFrameInfo &MFI = *MF.getFrameInfo();

  MFI.getObjectOffset(FrameRegIdx);
  llvm_unreachable("Unimplemented rewriteFrameIndex");
}

void llvm::emitRegUpdate(MachineBasicBlock &MBB,
                         MachineBasicBlock::iterator MBBI,
                         DebugLoc dl, const TargetInstrInfo &TII,
                         unsigned DstReg, unsigned SrcReg, unsigned ScratchReg,
                         int64_t NumBytes, MachineInstr::MIFlag MIFlags) {
  if (NumBytes == 0 && DstReg == SrcReg)
    return;
  else if (abs64(NumBytes) & ~0xffffff) {
    // Generically, we have to materialize the offset into a temporary register
    // and subtract it. There are a couple of ways this could be done, for now
    // we'll use a movz/movk or movn/movk sequence.
    uint64_t Bits = static_cast<uint64_t>(abs64(NumBytes));
    BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVZxii), ScratchReg)
      .addImm(0xffff & Bits).addImm(0)
      .setMIFlags(MIFlags);

    Bits >>= 16;
    if (Bits & 0xffff) {
      BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVKxii), ScratchReg)
        .addReg(ScratchReg)
        .addImm(0xffff & Bits).addImm(1)
        .setMIFlags(MIFlags);
    }

    Bits >>= 16;
    if (Bits & 0xffff) {
      BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVKxii), ScratchReg)
        .addReg(ScratchReg)
        .addImm(0xffff & Bits).addImm(2)
        .setMIFlags(MIFlags);
    }

    Bits >>= 16;
    if (Bits & 0xffff) {
      BuildMI(MBB, MBBI, dl, TII.get(AArch64::MOVKxii), ScratchReg)
        .addReg(ScratchReg)
        .addImm(0xffff & Bits).addImm(3)
        .setMIFlags(MIFlags);
    }

    // ADD DST, SRC, xTMP (, lsl #0)
    unsigned AddOp = NumBytes > 0 ? AArch64::ADDxxx_uxtx : AArch64::SUBxxx_uxtx;
    BuildMI(MBB, MBBI, dl, TII.get(AddOp), DstReg)
      .addReg(SrcReg, RegState::Kill)
      .addReg(ScratchReg, RegState::Kill)
      .addImm(0)
      .setMIFlag(MIFlags);
    return;
  }

  // Now we know that the adjustment can be done in at most two add/sub
  // (immediate) instructions, which is always more efficient than a
  // literal-pool load, or even a hypothetical movz/movk/add sequence

  // Decide whether we're doing addition or subtraction
  unsigned LowOp, HighOp;
  if (NumBytes >= 0) {
    LowOp = AArch64::ADDxxi_lsl0_s;
    HighOp = AArch64::ADDxxi_lsl12_s;
  } else {
    LowOp = AArch64::SUBxxi_lsl0_s;
    HighOp = AArch64::SUBxxi_lsl12_s;
    NumBytes = abs64(NumBytes);
  }

  // If we're here, at the very least a move needs to be produced, which just
  // happens to be materializable by an ADD.
  if ((NumBytes & 0xfff) || NumBytes == 0) {
    BuildMI(MBB, MBBI, dl, TII.get(LowOp), DstReg)
      .addReg(SrcReg, RegState::Kill)
      .addImm(NumBytes & 0xfff)
      .setMIFlag(MIFlags);

    // Next update should use the register we've just defined.
    SrcReg = DstReg;
  }

  if (NumBytes & 0xfff000) {
    BuildMI(MBB, MBBI, dl, TII.get(HighOp), DstReg)
      .addReg(SrcReg, RegState::Kill)
      .addImm(NumBytes >> 12)
      .setMIFlag(MIFlags);
  }
}

void llvm::emitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
                        DebugLoc dl, const TargetInstrInfo &TII,
                        unsigned ScratchReg, int64_t NumBytes,
                        MachineInstr::MIFlag MIFlags) {
  emitRegUpdate(MBB, MI, dl, TII, AArch64::XSP, AArch64::XSP, AArch64::X16,
                NumBytes, MIFlags);
}


namespace {
  struct LDTLSCleanup : public MachineFunctionPass {
    static char ID;
    LDTLSCleanup() : MachineFunctionPass(ID) {}

    virtual bool runOnMachineFunction(MachineFunction &MF) {
      AArch64MachineFunctionInfo* MFI
        = MF.getInfo<AArch64MachineFunctionInfo>();
      if (MFI->getNumLocalDynamicTLSAccesses() < 2) {
        // No point folding accesses if there isn't at least two.
        return false;
      }

      MachineDominatorTree *DT = &getAnalysis<MachineDominatorTree>();
      return VisitNode(DT->getRootNode(), 0);
    }

    // Visit the dominator subtree rooted at Node in pre-order.
    // If TLSBaseAddrReg is non-null, then use that to replace any
    // TLS_base_addr instructions. Otherwise, create the register
    // when the first such instruction is seen, and then use it
    // as we encounter more instructions.
    bool VisitNode(MachineDomTreeNode *Node, unsigned TLSBaseAddrReg) {
      MachineBasicBlock *BB = Node->getBlock();
      bool Changed = false;

      // Traverse the current block.
      for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end(); I != E;
           ++I) {
        switch (I->getOpcode()) {
        case AArch64::TLSDESC_BLRx:
          // Make sure it's a local dynamic access.
          if (!I->getOperand(1).isSymbol() ||
              strcmp(I->getOperand(1).getSymbolName(), "_TLS_MODULE_BASE_"))
            break;

          if (TLSBaseAddrReg)
            I = ReplaceTLSBaseAddrCall(I, TLSBaseAddrReg);
          else
            I = SetRegister(I, &TLSBaseAddrReg);
          Changed = true;
          break;
        default:
          break;
        }
      }

      // Visit the children of this block in the dominator tree.
      for (MachineDomTreeNode::iterator I = Node->begin(), E = Node->end();
           I != E; ++I) {
        Changed |= VisitNode(*I, TLSBaseAddrReg);
      }

      return Changed;
    }

    // Replace the TLS_base_addr instruction I with a copy from
    // TLSBaseAddrReg, returning the new instruction.
    MachineInstr *ReplaceTLSBaseAddrCall(MachineInstr *I,
                                         unsigned TLSBaseAddrReg) {
      MachineFunction *MF = I->getParent()->getParent();
      const AArch64TargetMachine *TM =
          static_cast<const AArch64TargetMachine *>(&MF->getTarget());
      const AArch64InstrInfo *TII = TM->getInstrInfo();

      // Insert a Copy from TLSBaseAddrReg to x0, which is where the rest of the
      // code sequence assumes the address will be.
      MachineInstr *Copy = BuildMI(*I->getParent(), I, I->getDebugLoc(),
                                   TII->get(TargetOpcode::COPY),
                                   AArch64::X0)
        .addReg(TLSBaseAddrReg);

      // Erase the TLS_base_addr instruction.
      I->eraseFromParent();

      return Copy;
    }

    // Create a virtal register in *TLSBaseAddrReg, and populate it by
    // inserting a copy instruction after I. Returns the new instruction.
    MachineInstr *SetRegister(MachineInstr *I, unsigned *TLSBaseAddrReg) {
      MachineFunction *MF = I->getParent()->getParent();
      const AArch64TargetMachine *TM =
          static_cast<const AArch64TargetMachine *>(&MF->getTarget());
      const AArch64InstrInfo *TII = TM->getInstrInfo();

      // Create a virtual register for the TLS base address.
      MachineRegisterInfo &RegInfo = MF->getRegInfo();
      *TLSBaseAddrReg = RegInfo.createVirtualRegister(&AArch64::GPR64RegClass);

      // Insert a copy from X0 to TLSBaseAddrReg for later.
      MachineInstr *Next = I->getNextNode();
      MachineInstr *Copy = BuildMI(*I->getParent(), Next, I->getDebugLoc(),
                                   TII->get(TargetOpcode::COPY),
                                   *TLSBaseAddrReg)
        .addReg(AArch64::X0);

      return Copy;
    }

    virtual const char *getPassName() const {
      return "Local Dynamic TLS Access Clean-up";
    }

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<MachineDominatorTree>();
      MachineFunctionPass::getAnalysisUsage(AU);
    }
  };
}

char LDTLSCleanup::ID = 0;
FunctionPass*
llvm::createAArch64CleanupLocalDynamicTLSPass() { return new LDTLSCleanup(); }
