//===- MipsGOT.cpp --------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "MipsGOT.h"

#include <llvm/Support/Casting.h>

#include <mcld/LD/ResolveInfo.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/MsgHandling.h>

namespace {
  const size_t MipsGOT0Num = 1;
}

using namespace mcld;

//===----------------------------------------------------------------------===//
// MipsGOT
//===----------------------------------------------------------------------===//
MipsGOT::MipsGOT(LDSection& pSection)
  : GOT(pSection),
    m_pLocalNum(0),
    m_pLast(NULL)
{
  // Create GOT0 entries.
  reserve(MipsGOT0Num);

  // Skip GOT0 entries.
  iterator it = m_SectionData->begin();

  for (size_t i = 1; i < MipsGOT0Num; ++i) {
    assert((it != m_SectionData->end()) &&
           "Generation of GOT0 entries is incomplete!");

    ++it;
  }

  m_LocalGOTIterator = it;
  m_GlobalGOTIterator = it;
  m_pLocalNum = MipsGOT0Num;
}

void MipsGOT::reserve(size_t pNum)
{
  for (size_t i = 0; i < pNum; i++) {
    new MipsGOTEntry(0, m_SectionData);
  }
}

MipsGOTEntry* MipsGOT::consume()
{
  if (NULL == m_pLast) {
    assert(!empty() && "Consume empty GOT entry!");
    m_pLast = llvm::cast<MipsGOTEntry>(&m_SectionData->front());
    return m_pLast;
  }

  m_pLast = llvm::cast<MipsGOTEntry>(m_pLast->getNextNode());
  return m_pLast;
}

bool MipsGOT::hasGOT1() const
{
  return (m_SectionData->size() > MipsGOT0Num);
}

uint64_t MipsGOT::emit(MemoryRegion& pRegion)
{
  uint32_t* buffer = reinterpret_cast<uint32_t*>(pRegion.getBuffer());

  uint64_t result = 0;
  for (iterator it = begin(), ie = end();
       it != ie; ++it, ++buffer) {
    MipsGOTEntry* got = &(llvm::cast<MipsGOTEntry>((*it)));
    *buffer = static_cast<uint32_t>(got->getValue());
    result += got->size();
  }
  return result;
}

void MipsGOT::reserveLocalEntry()
{
  reserve(1);
  ++m_pLocalNum;

  // Move global entries iterator forward.
  // We need to put global GOT entries after all local ones.
  ++m_GlobalGOTIterator;
}

void MipsGOT::reserveGlobalEntry()
{
  reserve(1);
}

MipsGOTEntry* MipsGOT::consumeLocal()
{
  iterator& it = m_LocalGOTIterator;
  ++it;
  assert(it != m_SectionData->getFragmentList().end() &&
         "The number of GOT Entries and ResolveInfo doesn't match");
  return llvm::cast<MipsGOTEntry>(&(*it));
}

MipsGOTEntry* MipsGOT::consumeGlobal()
{
  iterator& it = m_GlobalGOTIterator;
  ++it;
  assert(it != m_SectionData->getFragmentList().end() &&
         "The number of GOT Entries and ResolveInfo doesn't match");
  return llvm::cast<MipsGOTEntry>(&(*it));
}

size_t MipsGOT::getTotalNum() const
{
  return m_SectionData->getFragmentList().size();
}

size_t MipsGOT::getLocalNum() const
{
  return m_pLocalNum;
}