//===--- DebugInfoSupport.cpp -- Utils for debug info support ---*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Utilities to preserve and parse debug info from LinkGraphs.
//
//===----------------------------------------------------------------------===//

#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h"

#include "llvm/Support/SmallVectorMemoryBuffer.h"

#define DEBUG_TYPE "orc"

using namespace llvm;
using namespace llvm::orc;
using namespace llvm::jitlink;

namespace {
static DenseSet<StringRef> DWARFSectionNames = {
#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \
  StringRef(ELF_NAME),
#include "llvm/BinaryFormat/Dwarf.def"
#undef HANDLE_DWARF_SECTION
};

// We might be able to drop relocations to symbols that do end up
// being pruned by the linker, but for now we just preserve all
static void preserveDWARFSection(LinkGraph &G, Section &Sec) {
  DenseMap<Block *, Symbol *> Preserved;
  for (auto Sym : Sec.symbols()) {
    auto [It, Inserted] = Preserved.try_emplace(&Sym->getBlock());
    if (Inserted || Sym->isLive())
      It->second = Sym;
  }
  for (auto Block : Sec.blocks()) {
    auto &PSym = Preserved[Block];
    if (!PSym)
      PSym = &G.addAnonymousSymbol(*Block, 0, 0, false, true);
    else if (!PSym->isLive())
      PSym->setLive(true);
  }
}

static SmallVector<char, 0> getSectionData(Section &Sec) {
  SmallVector<char, 0> SecData;
  SmallVector<Block *, 8> SecBlocks(Sec.blocks().begin(), Sec.blocks().end());
  std::sort(SecBlocks.begin(), SecBlocks.end(), [](Block *LHS, Block *RHS) {
    return LHS->getAddress() < RHS->getAddress();
  });
  // Convert back to what object file would have, one blob of section content
  // Assumes all zerofill
  // TODO handle alignment?
  // TODO handle alignment offset?
  for (auto *Block : SecBlocks) {
    if (Block->isZeroFill())
      SecData.resize(SecData.size() + Block->getSize(), 0);
    else
      SecData.append(Block->getContent().begin(), Block->getContent().end());
  }
  return SecData;
}

static void dumpDWARFContext(DWARFContext &DC) {
  auto options = llvm::DIDumpOptions();
  options.DumpType &= ~DIDT_UUID;
  options.DumpType &= ~(1 << DIDT_ID_DebugFrame);
  LLVM_DEBUG(DC.dump(dbgs(), options));
}

} // namespace

Error llvm::orc::preserveDebugSections(LinkGraph &G) {
  if (!G.getTargetTriple().isOSBinFormatELF()) {
    return make_error<StringError>(
        "preserveDebugSections only supports ELF LinkGraphs!",
        inconvertibleErrorCode());
  }
  for (auto &Sec : G.sections()) {
    if (DWARFSectionNames.count(Sec.getName())) {
      LLVM_DEBUG(dbgs() << "Preserving DWARF section " << Sec.getName()
                        << "\n");
      preserveDWARFSection(G, Sec);
    }
  }
  return Error::success();
}

Expected<std::pair<std::unique_ptr<DWARFContext>,
                   StringMap<std::unique_ptr<MemoryBuffer>>>>
llvm::orc::createDWARFContext(LinkGraph &G) {
  if (!G.getTargetTriple().isOSBinFormatELF()) {
    return make_error<StringError>(
        "createDWARFContext only supports ELF LinkGraphs!",
        inconvertibleErrorCode());
  }
  StringMap<std::unique_ptr<MemoryBuffer>> DWARFSectionData;
  for (auto &Sec : G.sections()) {
    if (DWARFSectionNames.count(Sec.getName())) {
      auto SecData = getSectionData(Sec);
      auto Name = Sec.getName();
      // DWARFContext expects the section name to not start with a dot
      Name.consume_front(".");
      LLVM_DEBUG(dbgs() << "Creating DWARFContext section " << Name
                        << " with size " << SecData.size() << "\n");
      DWARFSectionData[Name] =
          std::make_unique<SmallVectorMemoryBuffer>(std::move(SecData));
    }
  }
  auto Ctx =
      DWARFContext::create(DWARFSectionData, G.getPointerSize(),
                           G.getEndianness() == llvm::endianness::little);
  dumpDWARFContext(*Ctx);
  return std::make_pair(std::move(Ctx), std::move(DWARFSectionData));
}
