/*******************************************************\
* Copyright (C) 2006, ApS s.r.o Brno, AllRightsReserved *
\*******************************************************/

#define DEBUG_TYPE "codasip-reg-info"

#include "llvm/Function.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetFrameLowering.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"

#include "CodasipMachineFunction.h"
#include "CodasipGenRegisterInfo.h"
#include "CodasipInstrInfo.h"

#define GET_REGINFO_TARGET_DESC
#include "CodasipGenRegisterInfo.inc"

#include <cstdio>

using namespace llvm;

CodasipGen2RegisterInfo::CodasipGen2RegisterInfo(const TargetInstrInfo &tii)
: CodasipGenRegisterInfo(0/*RA*/), TII(tii) {}

const unsigned int* CodasipGen2RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const
{
  static const unsigned CalleeSavedRegs[] = {Codasip::gpregs_1, 0};
  return CalleeSavedRegs;
}

BitVector CodasipGen2RegisterInfo::getReservedRegs(const MachineFunction &MF) const
{
  BitVector Reserved(getNumRegs());
  Reserved.set(Codasip::gpregs_0);
  Reserved.set(Codasip::gpregs_1);

  return Reserved;
}

// rearrange if out of bounds
void CodasipGen2RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, RegScavenger *RS) const
{
  MachineInstr &MI = *II;
  MachineBasicBlock &MB = *MI.getParent();
  MachineFunction &MF = *MB.getParent();
  //MachineRegisterInfo &MRI = MF.getRegInfo();
  bool vararg = MF.getFunction()->isVarArg();
  CodasipFunctionInfo *finfo = MF.getInfo<CodasipFunctionInfo>();
  DebugLoc dl = MI.getDebugLoc();
  unsigned i = 0;
  while (!MI.getOperand(i).isFI()) {
    assert(i < MI.getNumOperands() && "Instr doesn't have FrameIndex operand!");
    ++i;
  }
  // get information
  assert(SPAdj==0);
  int FrameIndex = MI.getOperand(i).getIndex();
  int StackSize = MF.getFrameInfo()->getStackSize();
  int Offset = MF.getFrameInfo()->getObjectOffset(FrameIndex);
  bool overflow = false;
  // fixed objects (we created them in the lowering for passing and receiving of arguments)
  if (FrameIndex<0) {
    // outgoing argument (placed at the end of the caller's space)
    if (Offset<=0) {
      // (if stack grows down)
      Offset = -(StackSize+Offset);
    }
    // (incomming arguments shifted in lowering already)
  }
  else {
    // should be negative (if the stack grows down)
    assert(Offset<0);
  }
  // is it an index of a vararg backup array?
  if (vararg) {
    finfo->ArgsInfo.SetBackupOff(FrameIndex,Offset);
  }
  // is there a two-address constraint?
  const MCInstrDesc &desc = MI.getDesc();
  if (desc.getOperandConstraint(i,MCOI::TIED_TO) >= 0) {
    // get the result register
    const MachineOperand &dst = MI.getOperand(0);
    assert(dst.isReg() && dst.isDef());
    // copy the frame reg first (inserts before II)
    BuildMI(MB,II,dl, TII.get(Codasip::i_2_reg_ops__opc_mov__rmem__rmem__), dst.getReg()).addReg(getFrameRegister(MF));
    // then mutate the instruction
    MI.getOperand(i).ChangeToRegister(dst.getReg(),false);
  }
  else {
    // just mutate the instruction
    MI.getOperand(i).ChangeToRegister(getFrameRegister(MF),false);
  }
  ++i;
  assert(i < MI.getNumOperands() && "There aren't enough operands!");
  // indirect addressing of local space
  if (MI.getOperand(i).isReg()) {
    unsigned reg = MI.getOperand(i).getReg();
    // add the offset to the reg
    // (this is the only way that's truly safe and universal)
    
    goto error;
  }
  // it should be imm otherwise
  else {
    assert(MI.getOperand(i).isImm() && "Bad usage of a frame index!");
  }
  // add the value that is already there (usually 0)
  Offset += MI.getOperand(i).getImm();
  // check limit (sum of the frame index and the offset)
  switch (MI.getOpcode())
  {
    case Codasip::i_arithm_imm__opc_addi__rmem__rmem__uimm16__:
      overflow = ((Offset&4294934528)!=4294934528 && (Offset&4294934528)!=0);
      break;
    case Codasip::i_load_store__opc_load__rmem__rmem__simm16__:
      overflow = ((Offset&4294934528)!=4294934528 && (Offset&4294934528)!=0);
      break;
    case Codasip::i_load_store__opc_store__rmem__rmem__simm16__:
      overflow = ((Offset&4294934528)!=4294934528 && (Offset&4294934528)!=0);
      break;
    
    default:
      assert(false && "unreachable");
  }
  // overflow?
  if (overflow) {
      goto error;
      Offset = 0;
  }
  // set the the offset operand
  MI.getOperand(i).setImm(Offset);
  return;
  // handle error
  error:
  const Function *fn = MF.getFunction();
  std::string fnn = fn->getNameStr();
  errs() << "Failed to address the local space of '"<<fnn<<"'.\n";
  exit(1);
}

int CodasipGen2RegisterInfo::getDwarfRegNum(unsigned RegNum, bool isEH) const
{
  return RegNum;
}

// (base pointer)
unsigned CodasipGen2RegisterInfo::getFrameRegister(const MachineFunction &MF) const
{
  // base pointer
  return Codasip::gpregs_1;
}

// (return address register - like on mips)
unsigned CodasipGen2RegisterInfo::getRARegister() const
{
  return Codasip::gpregs_7;
}

// we detect only one pointer class
const TargetRegisterClass* CodasipGen2RegisterInfo::getPointerRegClass(unsigned Kind) const
{
  return &Codasip::gpregsRegClass;
}

// eliminate call start/end (and other things)
void CodasipGen2RegisterInfo::eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MB, MachineBasicBlock::iterator II) const
{
  assert(II->getOpcode()==Codasip::CALLSTART || II->getOpcode()==Codasip::CALLEND);
  const TargetFrameLowering *TFI = MF.getTarget().getFrameLowering();
  if (!TFI->hasReservedCallFrame(MF))
  {
    DebugLoc dl = II->getDebugLoc();
    // we don't usually get here, but it does happen
    // (e.g. when "dynamic" local arrays are used)
    // is it start or end?
    bool is_start = II->getOpcode()==Codasip::CALLSTART;
    // get stack offset
    assert(II->getOperand(0).isImm());
    int off = II->getOperand(0).getImm();
    assert(off>=0);
    if (off)
    {
      if (is_start) off = -off;
      // adjust SP register
      BuildMI(MB,II,dl, TII.get(Codasip::i_arithm_imm__opc_addi__rmem__rmem__uimm16__), Codasip::gpregs_0).addReg(Codasip::gpregs_0).addImm(off);
    }
  }
  // erase in any case
  MB.erase(II);
}

