//===- SymbolRecordMapping.cpp -----------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h"

using namespace llvm;
using namespace llvm::codeview;

#define error(X)                                                               \
  if (auto EC = X)                                                             \
    return EC;

namespace {
struct MapGap {
  Error operator()(CodeViewRecordIO &IO, LocalVariableAddrGap &Gap) const {
    error(IO.mapInteger(Gap.GapStartOffset));
    error(IO.mapInteger(Gap.Range));
    return Error::success();
  }
};
}

static Error mapLocalVariableAddrRange(CodeViewRecordIO &IO,
                                       LocalVariableAddrRange &Range) {
  error(IO.mapInteger(Range.OffsetStart));
  error(IO.mapInteger(Range.ISectStart));
  error(IO.mapInteger(Range.Range));
  return Error::success();
}

Error SymbolRecordMapping::visitSymbolBegin(CVSymbol &Record) {
  error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix)));
  return Error::success();
}

Error SymbolRecordMapping::visitSymbolEnd(CVSymbol &Record) {
  error(IO.padToAlignment(alignOf(Container)));
  error(IO.endRecord());
  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) {

  error(IO.mapInteger(Block.Parent));
  error(IO.mapInteger(Block.End));
  error(IO.mapInteger(Block.CodeSize));
  error(IO.mapInteger(Block.CodeOffset));
  error(IO.mapInteger(Block.Segment));
  error(IO.mapStringZ(Block.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, Thunk32Sym &Thunk) {

  error(IO.mapInteger(Thunk.Parent));
  error(IO.mapInteger(Thunk.End));
  error(IO.mapInteger(Thunk.Next));
  error(IO.mapInteger(Thunk.Offset));
  error(IO.mapInteger(Thunk.Segment));
  error(IO.mapInteger(Thunk.Length));
  error(IO.mapEnum(Thunk.Thunk));
  error(IO.mapStringZ(Thunk.Name));
  error(IO.mapByteVectorTail(Thunk.VariantData));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            TrampolineSym &Tramp) {

  error(IO.mapEnum(Tramp.Type));
  error(IO.mapInteger(Tramp.Size));
  error(IO.mapInteger(Tramp.ThunkOffset));
  error(IO.mapInteger(Tramp.TargetOffset));
  error(IO.mapInteger(Tramp.ThunkSection));
  error(IO.mapInteger(Tramp.TargetSection));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            SectionSym &Section) {
  uint8_t Padding = 0;

  error(IO.mapInteger(Section.SectionNumber));
  error(IO.mapInteger(Section.Alignment));
  error(IO.mapInteger(Padding));
  error(IO.mapInteger(Section.Rva));
  error(IO.mapInteger(Section.Length));
  error(IO.mapInteger(Section.Characteristics));
  error(IO.mapStringZ(Section.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            CoffGroupSym &CoffGroup) {

  error(IO.mapInteger(CoffGroup.Size));
  error(IO.mapInteger(CoffGroup.Characteristics));
  error(IO.mapInteger(CoffGroup.Offset));
  error(IO.mapInteger(CoffGroup.Segment));
  error(IO.mapStringZ(CoffGroup.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            BPRelativeSym &BPRel) {

  error(IO.mapInteger(BPRel.Offset));
  error(IO.mapInteger(BPRel.Type));
  error(IO.mapStringZ(BPRel.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            BuildInfoSym &BuildInfo) {

  error(IO.mapInteger(BuildInfo.BuildId));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            CallSiteInfoSym &CallSiteInfo) {
  uint16_t Padding = 0;

  error(IO.mapInteger(CallSiteInfo.CodeOffset));
  error(IO.mapInteger(CallSiteInfo.Segment));
  error(IO.mapInteger(Padding));
  error(IO.mapInteger(CallSiteInfo.Type));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            EnvBlockSym &EnvBlock) {

  uint8_t Reserved = 0;
  error(IO.mapInteger(Reserved));
  error(IO.mapStringZVectorZ(EnvBlock.Fields));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            FileStaticSym &FileStatic) {

  error(IO.mapInteger(FileStatic.Index));
  error(IO.mapInteger(FileStatic.ModFilenameOffset));
  error(IO.mapEnum(FileStatic.Flags));
  error(IO.mapStringZ(FileStatic.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, ExportSym &Export) {

  error(IO.mapInteger(Export.Ordinal));
  error(IO.mapEnum(Export.Flags));
  error(IO.mapStringZ(Export.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            Compile2Sym &Compile2) {

  error(IO.mapEnum(Compile2.Flags));
  error(IO.mapEnum(Compile2.Machine));
  error(IO.mapInteger(Compile2.VersionFrontendMajor));
  error(IO.mapInteger(Compile2.VersionFrontendMinor));
  error(IO.mapInteger(Compile2.VersionFrontendBuild));
  error(IO.mapInteger(Compile2.VersionBackendMajor));
  error(IO.mapInteger(Compile2.VersionBackendMinor));
  error(IO.mapInteger(Compile2.VersionBackendBuild));
  error(IO.mapStringZ(Compile2.Version));
  error(IO.mapStringZVectorZ(Compile2.ExtraStrings));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            Compile3Sym &Compile3) {

  error(IO.mapEnum(Compile3.Flags));
  error(IO.mapEnum(Compile3.Machine));
  error(IO.mapInteger(Compile3.VersionFrontendMajor));
  error(IO.mapInteger(Compile3.VersionFrontendMinor));
  error(IO.mapInteger(Compile3.VersionFrontendBuild));
  error(IO.mapInteger(Compile3.VersionFrontendQFE));
  error(IO.mapInteger(Compile3.VersionBackendMajor));
  error(IO.mapInteger(Compile3.VersionBackendMinor));
  error(IO.mapInteger(Compile3.VersionBackendBuild));
  error(IO.mapInteger(Compile3.VersionBackendQFE));
  error(IO.mapStringZ(Compile3.Version));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            ConstantSym &Constant) {

  error(IO.mapInteger(Constant.Type));
  error(IO.mapEncodedInteger(Constant.Value));
  error(IO.mapStringZ(Constant.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, DataSym &Data) {

  error(IO.mapInteger(Data.Type));
  error(IO.mapInteger(Data.DataOffset));
  error(IO.mapInteger(Data.Segment));
  error(IO.mapStringZ(Data.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR, DefRangeFramePointerRelSym &DefRangeFramePointerRel) {

  error(IO.mapInteger(DefRangeFramePointerRel.Offset));
  error(mapLocalVariableAddrRange(IO, DefRangeFramePointerRel.Range));
  error(IO.mapVectorTail(DefRangeFramePointerRel.Gaps, MapGap()));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR,
    DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) {

  error(IO.mapInteger(DefRangeFramePointerRelFullScope.Offset));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR, DefRangeRegisterRelSym &DefRangeRegisterRel) {

  error(IO.mapObject(DefRangeRegisterRel.Hdr.Register));
  error(IO.mapObject(DefRangeRegisterRel.Hdr.Flags));
  error(IO.mapObject(DefRangeRegisterRel.Hdr.BasePointerOffset));
  error(mapLocalVariableAddrRange(IO, DefRangeRegisterRel.Range));
  error(IO.mapVectorTail(DefRangeRegisterRel.Gaps, MapGap()));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) {

  error(IO.mapObject(DefRangeRegister.Hdr.Register));
  error(IO.mapObject(DefRangeRegister.Hdr.MayHaveNoName));
  error(mapLocalVariableAddrRange(IO, DefRangeRegister.Range));
  error(IO.mapVectorTail(DefRangeRegister.Gaps, MapGap()));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) {

  error(IO.mapObject(DefRangeSubfieldRegister.Hdr.Register));
  error(IO.mapObject(DefRangeSubfieldRegister.Hdr.MayHaveNoName));
  error(IO.mapObject(DefRangeSubfieldRegister.Hdr.OffsetInParent));
  error(mapLocalVariableAddrRange(IO, DefRangeSubfieldRegister.Range));
  error(IO.mapVectorTail(DefRangeSubfieldRegister.Gaps, MapGap()));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR, DefRangeSubfieldSym &DefRangeSubfield) {

  error(IO.mapInteger(DefRangeSubfield.Program));
  error(IO.mapInteger(DefRangeSubfield.OffsetInParent));
  error(mapLocalVariableAddrRange(IO, DefRangeSubfield.Range));
  error(IO.mapVectorTail(DefRangeSubfield.Gaps, MapGap()));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            DefRangeSym &DefRange) {

  error(IO.mapInteger(DefRange.Program));
  error(mapLocalVariableAddrRange(IO, DefRange.Range));
  error(IO.mapVectorTail(DefRange.Gaps, MapGap()));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            FrameCookieSym &FrameCookie) {

  error(IO.mapInteger(FrameCookie.CodeOffset));
  error(IO.mapInteger(FrameCookie.Register));
  error(IO.mapEnum(FrameCookie.CookieKind));
  error(IO.mapInteger(FrameCookie.Flags));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            FrameProcSym &FrameProc) {
  error(IO.mapInteger(FrameProc.TotalFrameBytes));
  error(IO.mapInteger(FrameProc.PaddingFrameBytes));
  error(IO.mapInteger(FrameProc.OffsetToPadding));
  error(IO.mapInteger(FrameProc.BytesOfCalleeSavedRegisters));
  error(IO.mapInteger(FrameProc.OffsetOfExceptionHandler));
  error(IO.mapInteger(FrameProc.SectionIdOfExceptionHandler));
  error(IO.mapEnum(FrameProc.Flags));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(
    CVSymbol &CVR, HeapAllocationSiteSym &HeapAllocSite) {

  error(IO.mapInteger(HeapAllocSite.CodeOffset));
  error(IO.mapInteger(HeapAllocSite.Segment));
  error(IO.mapInteger(HeapAllocSite.CallInstructionSize));
  error(IO.mapInteger(HeapAllocSite.Type));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            InlineSiteSym &InlineSite) {

  error(IO.mapInteger(InlineSite.Parent));
  error(IO.mapInteger(InlineSite.End));
  error(IO.mapInteger(InlineSite.Inlinee));
  error(IO.mapByteVectorTail(InlineSite.AnnotationData));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            RegisterSym &Register) {

  error(IO.mapInteger(Register.Index));
  error(IO.mapEnum(Register.Register));
  error(IO.mapStringZ(Register.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            PublicSym32 &Public) {

  error(IO.mapEnum(Public.Flags));
  error(IO.mapInteger(Public.Offset));
  error(IO.mapInteger(Public.Segment));
  error(IO.mapStringZ(Public.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            ProcRefSym &ProcRef) {

  error(IO.mapInteger(ProcRef.SumName));
  error(IO.mapInteger(ProcRef.SymOffset));
  error(IO.mapInteger(ProcRef.Module));
  error(IO.mapStringZ(ProcRef.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, LabelSym &Label) {

  error(IO.mapInteger(Label.CodeOffset));
  error(IO.mapInteger(Label.Segment));
  error(IO.mapEnum(Label.Flags));
  error(IO.mapStringZ(Label.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, LocalSym &Local) {
  error(IO.mapInteger(Local.Type));
  error(IO.mapEnum(Local.Flags));
  error(IO.mapStringZ(Local.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            ObjNameSym &ObjName) {

  error(IO.mapInteger(ObjName.Signature));
  error(IO.mapStringZ(ObjName.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) {
  error(IO.mapInteger(Proc.Parent));
  error(IO.mapInteger(Proc.End));
  error(IO.mapInteger(Proc.Next));
  error(IO.mapInteger(Proc.CodeSize));
  error(IO.mapInteger(Proc.DbgStart));
  error(IO.mapInteger(Proc.DbgEnd));
  error(IO.mapInteger(Proc.FunctionType));
  error(IO.mapInteger(Proc.CodeOffset));
  error(IO.mapInteger(Proc.Segment));
  error(IO.mapEnum(Proc.Flags));
  error(IO.mapStringZ(Proc.Name));
  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            ScopeEndSym &ScopeEnd) {
  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) {
  error(IO.mapVectorN<uint32_t>(
      Caller.Indices,
      [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            RegRelativeSym &RegRel) {

  error(IO.mapInteger(RegRel.Offset));
  error(IO.mapInteger(RegRel.Type));
  error(IO.mapEnum(RegRel.Register));
  error(IO.mapStringZ(RegRel.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            ThreadLocalDataSym &Data) {

  error(IO.mapInteger(Data.Type));
  error(IO.mapInteger(Data.DataOffset));
  error(IO.mapInteger(Data.Segment));
  error(IO.mapStringZ(Data.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR, UDTSym &UDT) {

  error(IO.mapInteger(UDT.Type));
  error(IO.mapStringZ(UDT.Name));

  return Error::success();
}

Error SymbolRecordMapping::visitKnownRecord(CVSymbol &CVR,
                                            UsingNamespaceSym &UN) {

  error(IO.mapStringZ(UN.Name));

  return Error::success();
}

RegisterId codeview::decodeFramePtrReg(EncodedFramePtrReg EncodedReg,
                                       CPUType CPU) {
  assert(unsigned(EncodedReg) < 4);
  switch (CPU) {
  // FIXME: Add ARM and AArch64 variants here.
  default:
    break;
  case CPUType::Intel8080:
  case CPUType::Intel8086:
  case CPUType::Intel80286:
  case CPUType::Intel80386:
  case CPUType::Intel80486:
  case CPUType::Pentium:
  case CPUType::PentiumPro:
  case CPUType::Pentium3:
    switch (EncodedReg) {
    case EncodedFramePtrReg::None:     return RegisterId::NONE;
    case EncodedFramePtrReg::StackPtr: return RegisterId::VFRAME;
    case EncodedFramePtrReg::FramePtr: return RegisterId::EBP;
    case EncodedFramePtrReg::BasePtr:  return RegisterId::EBX;
    }
    llvm_unreachable("bad encoding");
  case CPUType::X64:
    switch (EncodedReg) {
    case EncodedFramePtrReg::None:     return RegisterId::NONE;
    case EncodedFramePtrReg::StackPtr: return RegisterId::RSP;
    case EncodedFramePtrReg::FramePtr: return RegisterId::RBP;
    case EncodedFramePtrReg::BasePtr:  return RegisterId::R13;
    }
    llvm_unreachable("bad encoding");
  }
  return RegisterId::NONE;
}

EncodedFramePtrReg codeview::encodeFramePtrReg(RegisterId Reg, CPUType CPU) {
  switch (CPU) {
  // FIXME: Add ARM and AArch64 variants here.
  default:
    break;
  case CPUType::Intel8080:
  case CPUType::Intel8086:
  case CPUType::Intel80286:
  case CPUType::Intel80386:
  case CPUType::Intel80486:
  case CPUType::Pentium:
  case CPUType::PentiumPro:
  case CPUType::Pentium3:
    switch (Reg) {
    case RegisterId::VFRAME:
      return EncodedFramePtrReg::StackPtr;
    case RegisterId::EBP:
      return EncodedFramePtrReg::FramePtr;
    case RegisterId::EBX:
      return EncodedFramePtrReg::BasePtr;
    default:
      break;
    }
    break;
  case CPUType::X64:
    switch (Reg) {
    case RegisterId::RSP:
      return EncodedFramePtrReg::StackPtr;
    case RegisterId::RBP:
      return EncodedFramePtrReg::FramePtr;
    case RegisterId::R13:
      return EncodedFramePtrReg::BasePtr;
    default:
      break;
    }
    break;
  }
  return EncodedFramePtrReg::None;
}
