[llvm] e7950fc - [llvm-debuginfo-analyzer] (09/09) - CodeView Reader

Carlos Alberto Enciso via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 27 01:25:25 PST 2023


Author: Carlos Alberto Enciso
Date: 2023-02-27T09:15:43Z
New Revision: e7950fceb1e7f82370f6cff80b258e552eb410a6

URL: https://github.com/llvm/llvm-project/commit/e7950fceb1e7f82370f6cff80b258e552eb410a6
DIFF: https://github.com/llvm/llvm-project/commit/e7950fceb1e7f82370f6cff80b258e552eb410a6.diff

LOG: [llvm-debuginfo-analyzer] (09/09) - CodeView Reader

llvm-debuginfo-analyzer is a command line tool that processes debug
info contained in a binary file and produces a debug information
format agnostic “Logical View”, which is a high-level semantic
representation of the debug info, independent of the low-level
format.

The code has been divided into the following patches:

1) Interval tree
2) Driver and documentation
3) Logical elements
4) Locations and ranges
5) Select elements
6) Warning and internal options
7) Compare elements
8) ELF Reader
9) CodeView Reader

Full details:
https://discourse.llvm.org/t/llvm-dev-rfc-llvm-dva-debug-information-visual-analyzer/62570

This patch:

This is a high level summary of the changes in this patch.

CodeView Reader
- Support for CodeView/PDB.
  LVCodeViewReader, LVTypeVisitor, LVSymbolVisitor, LVLogicalVisitor

Reviewed By: psamolysov, probinson, djtodoro, zequanwu

Differential Revision: https://reviews.llvm.org/D125784

Added: 
    llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h
    llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h
    llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp
    llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-clang.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-msvc.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-clang.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-msvc.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-clang.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-msvc.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-clang.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-msvc.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-clang.o
    llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-msvc.o
    llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp
    llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-clang.o
    llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-msvc.o
    llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc-.pdb
    llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc.o

Modified: 
    llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h
    llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
    llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
    llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h
    llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h
    llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h
    llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
    llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h
    llvm/lib/DebugInfo/LogicalView/CMakeLists.txt
    llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
    llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
    llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
    llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp
    llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp
    llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp
    llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
    llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp
    llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h
index 68dfefba3b3cd..17fa04040ad77 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h
@@ -205,6 +205,9 @@ class LVElement : public LVObject {
   size_t getNameIndex() const { return NameIndex; }
   size_t getQualifiedNameIndex() const { return QualifiedNameIndex; }
 
+  void setInnerComponent() { setInnerComponent(getName()); }
+  void setInnerComponent(StringRef Name);
+
   // Element type name.
   StringRef getTypeName() const;
 
@@ -253,7 +256,7 @@ class LVElement : public LVObject {
   virtual void setDiscriminator(uint32_t Value) {}
 
   // Process the values for a DW_TAG_enumerator.
-  virtual std::string getValue() const { return {}; }
+  virtual StringRef getValue() const { return {}; }
   virtual void setValue(StringRef Value) {}
   virtual size_t getValueIndex() const { return 0; }
 
@@ -263,6 +266,13 @@ class LVElement : public LVObject {
   StringRef
   accessibilityString(uint32_t Access = dwarf::DW_ACCESS_private) const;
 
+  // CodeView Accessibility Codes.
+  std::optional<uint32_t> getAccessibilityCode(codeview::MemberAccess Access);
+  void setAccessibilityCode(codeview::MemberAccess Access) {
+    if (std::optional<uint32_t> Code = getAccessibilityCode(Access))
+      AccessibilityCode = Code.value();
+  }
+
   // DWARF Inline Codes.
   uint32_t getInlineCode() const { return InlineCode; }
   void setInlineCode(uint32_t Code) { InlineCode = Code; }
@@ -274,6 +284,13 @@ class LVElement : public LVObject {
   StringRef
   virtualityString(uint32_t Virtuality = dwarf::DW_VIRTUALITY_none) const;
 
+  // CodeView Virtuality Codes.
+  std::optional<uint32_t> getVirtualityCode(codeview::MethodKind Virtuality);
+  void setVirtualityCode(codeview::MethodKind Virtuality) {
+    if (std::optional<uint32_t> Code = getVirtualityCode(Virtuality))
+      VirtualityCode = Code.value();
+  }
+
   // DWARF Extern Codes.
   StringRef externalString() const;
 

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
index ff1a41cdf3e5c..ea4c31af364ec 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h
@@ -250,6 +250,12 @@ class LVReader {
     assert(Scope && Scope->isCompileUnit() && "Scope is not a compile unit");
     CompileUnit = static_cast<LVScopeCompileUnit *>(Scope);
   }
+  void setCompileUnitCPUType(codeview::CPUType Type) {
+    CompileUnit->setCPUType(Type);
+  }
+  codeview::CPUType getCompileUnitCPUType() {
+    return CompileUnit->getCPUType();
+  }
 
   // Access to the scopes root.
   LVScopeRoot *getScopesRoot() const { return Root; }

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
index 4368e9334387b..1b3c377cd7dbb 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h
@@ -410,6 +410,9 @@ class LVScopeCompileUnit final : public LVScope {
   // Compilation directory name.
   size_t CompilationDirectoryIndex = 0;
 
+  // Used by the CodeView Reader.
+  codeview::CPUType CompilationCPUType = codeview::CPUType::X64;
+
   // Keep record of elements. They are needed at the compilation unit level
   // to print the summary at the end of the printing.
   LVCounter Allocated;
@@ -537,6 +540,9 @@ class LVScopeCompileUnit final : public LVScope {
     ProducerIndex = getStringPool().getIndex(ProducerName);
   }
 
+  void setCPUType(codeview::CPUType Type) { CompilationCPUType = Type; }
+  codeview::CPUType getCPUType() { return CompilationCPUType; }
+
   // Record DWARF tags.
   void addDebugTag(dwarf::Tag Target, LVOffset Offset);
   // Record elements with invalid offsets.
@@ -789,6 +795,10 @@ class LVScopeRoot final : public LVScope {
     FileFormatNameIndex = getStringPool().getIndex(FileFormatName);
   }
 
+  // The CodeView Reader uses scoped names. Recursively transform the
+  // element name to use just the most inner component.
+  void transformScopedName();
+
   // Process the collected location, ranges and calculate coverage.
   void processRangeInformation();
 

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h
index d65ac641afbe5..50f2c9a09ff5d 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h
@@ -31,6 +31,11 @@ namespace logicalview {
 // Returns the unique string pool instance.
 LVStringPool &getStringPool();
 
+using LVStringRefs = std::vector<StringRef>;
+using LVLexicalComponent = std::tuple<StringRef, StringRef>;
+using LVLexicalIndex =
+    std::tuple<LVStringRefs::size_type, LVStringRefs::size_type>;
+
 // Used to record specific characteristics about the objects.
 template <typename T> class LVProperties {
   SmallBitVector Bits = SmallBitVector(static_cast<unsigned>(T::LastEntry) + 1);
@@ -221,6 +226,15 @@ inline std::string formattedNames(StringRef Name1, StringRef Name2) {
   return (Twine("'") + Twine(Name1) + Twine(Name2) + Twine("'")).str();
 }
 
+// The given string represents a symbol or type name with optional enclosing
+// scopes, such as: name, name<..>, scope::name, scope::..::name, etc.
+// The string can have multiple references to template instantiations.
+// It returns the inner most component.
+LVLexicalComponent getInnerComponent(StringRef Name);
+LVStringRefs getAllLexicalComponents(StringRef Name);
+std::string getScopedName(const LVStringRefs &Components,
+                          StringRef BaseName = {});
+
 // These are the values assigned to the debug location record IDs.
 // See DebugInfo/CodeView/CodeViewSymbols.def.
 // S_DEFRANGE                               0x113f

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h
index 4b2a910c88f1c..3d4d65b2251f8 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h
@@ -115,8 +115,8 @@ class LVSymbol final : public LVElement {
   void setBitSize(uint32_t Size) override { BitSize = Size; }
 
   // Process the values for a DW_AT_const_value.
-  std::string getValue() const override {
-    return std::string(getStringPool().getString(ValueIndex));
+  StringRef getValue() const override {
+    return getStringPool().getString(ValueIndex);
   }
   void setValue(StringRef Value) override {
     ValueIndex = getStringPool().getIndex(Value);

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h
index 4d377ce2ff878..28881b3c95b17 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h
@@ -182,8 +182,8 @@ class LVTypeEnumerator final : public LVType {
   ~LVTypeEnumerator() = default;
 
   // Process the values for a DW_TAG_enumerator.
-  std::string getValue() const override {
-    return std::string(getStringPool().getString(ValueIndex));
+  StringRef getValue() const override {
+    return getStringPool().getString(ValueIndex);
   }
   void setValue(StringRef Value) override {
     ValueIndex = getStringPool().getIndex(Value);
@@ -222,8 +222,8 @@ class LVTypeParam final : public LVType {
   ~LVTypeParam() = default;
 
   // Template parameter value.
-  std::string getValue() const override {
-    return std::string(getStringPool().getString(ValueIndex));
+  StringRef getValue() const override {
+    return getStringPool().getString(ValueIndex);
   }
   void setValue(StringRef Value) override {
     ValueIndex = getStringPool().getIndex(Value);

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h b/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
index 9f5e80747bf5e..bf30501d00c1f 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
@@ -58,6 +58,8 @@ class LVReaderHandler {
                    object::MachOUniversalBinary &Mach);
   Error handleObject(LVReaders &Readers, StringRef Filename,
                      object::Binary &Binary);
+  Error handleObject(LVReaders &Readers, StringRef Filename, StringRef Buffer,
+                     StringRef ExePath);
 
   Error createReader(StringRef Filename, LVReaders &Readers, PdbOrObj &Input,
                      StringRef FileFormatName, StringRef ExePath = {});

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h
index 4b2dc666733ee..a66cf4608823b 100644
--- a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h
@@ -24,6 +24,7 @@
 #include "llvm/MC/MCRegisterInfo.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/COFF.h"
 #include "llvm/Object/ObjectFile.h"
 
 namespace llvm {
@@ -69,6 +70,12 @@ class LVBinaryReader : public LVReader {
   // Function names extracted from the object symbol table.
   LVSymbolTable SymbolTable;
 
+  // It contains the LVLineDebug elements representing the inlined logical
+  // lines for the current compile unit, created by parsing the CodeView
+  // S_INLINESITE symbol annotation data.
+  using LVInlineeLine = std::map<LVScope *, std::unique_ptr<LVLines>>;
+  LVInlineeLine CUInlineeLines;
+
   // Instruction lines for a logical scope. These instructions are fetched
   // during its merge with the debug lines.
   LVDoubleMap<LVSectionIndex, LVScope *, LVLines *> ScopeInstructions;
@@ -135,6 +142,8 @@ class LVBinaryReader : public LVReader {
                        LVAddress LowerAddress, LVAddress UpperAddress);
   LVRange *getSectionRanges(LVSectionIndex SectionIndex);
 
+  void includeInlineeLines(LVSectionIndex SectionIndex, LVScope *Function);
+
   Error createInstructions();
   Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex);
   Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex,
@@ -153,6 +162,16 @@ class LVBinaryReader : public LVReader {
   LVBinaryReader &operator=(const LVBinaryReader &) = delete;
   virtual ~LVBinaryReader() = default;
 
+  void addInlineeLines(LVScope *Scope, LVLines &Lines) {
+    CUInlineeLines.emplace(Scope, std::make_unique<LVLines>(std::move(Lines)));
+  }
+
+  // Convert Segment::Offset pair to absolute address.
+  LVAddress linearAddress(uint16_t Segment, uint32_t Offset,
+                          LVAddress Addendum = 0) {
+    return ImageBaseAddress + (Segment * VirtualAddress) + Offset + Addendum;
+  }
+
   void addToSymbolTable(StringRef Name, LVScope *Function,
                         LVSectionIndex SectionIndex = 0);
   void addToSymbolTable(StringRef Name, LVAddress Address,

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h
new file mode 100644
index 0000000000000..35d37b03c3bbc
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h
@@ -0,0 +1,235 @@
+//===-- LVCodeViewReader.h --------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LVCodeViewReader class, which is used to describe a
+// debug information (COFF) reader.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H
+#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H
+
+#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h"
+#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Support/BinaryByteStream.h"
+#include "llvm/Support/BinaryItemStream.h"
+#include "llvm/Support/BinaryStreamArray.h"
+
+namespace llvm {
+template <> struct BinaryItemTraits<codeview::CVType> {
+  static size_t length(const codeview::CVType &Item) { return Item.length(); }
+  static ArrayRef<uint8_t> bytes(const codeview::CVType &Item) {
+    return Item.data();
+  }
+};
+
+namespace codeview {
+class LazyRandomTypeCollection;
+}
+namespace object {
+struct coff_section;
+}
+namespace pdb {
+class SymbolGroup;
+}
+namespace logicalview {
+
+class LVElement;
+class LVLine;
+class LVScope;
+class LVScopeCompileUnit;
+class LVSymbol;
+class LVType;
+class LVTypeVisitor;
+class LVSymbolVisitor;
+class LVSymbolVisitorDelegate;
+
+using LVNames = SmallVector<StringRef, 16>;
+
+// The ELF reader uses the DWARF constants to create the logical elements.
+// The DW_TAG_* and DW_AT_* are used to select the logical object and to
+// set specific attributes, such as name, type, etc.
+// As the CodeView constants are 
diff erent to the DWARF constants, the
+// CodeView reader will map them to the DWARF ones.
+
+class LVCodeViewReader final : public LVBinaryReader {
+  friend class LVTypeVisitor;
+  friend class LVSymbolVisitor;
+  friend class LVSymbolVisitorDelegate;
+
+  using LVModules = std::vector<LVScope *>;
+  LVModules Modules;
+
+  // Encapsulates access to the input file and any dependent type server,
+  // including any precompiled header object.
+  llvm::pdb::InputFile Input;
+  std::shared_ptr<llvm::pdb::InputFile> TypeServer;
+  std::shared_ptr<LazyRandomTypeCollection> PrecompHeader;
+
+  // Persistance data when loading a type server.
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = nullptr;
+  std::unique_ptr<MemoryBuffer> MemBuffer;
+  std::unique_ptr<llvm::pdb::IPDBSession> Session;
+  std::unique_ptr<llvm::pdb::NativeSession> PdbSession;
+
+  // Persistance data when loading a precompiled header.
+  BumpPtrAllocator BuilderAllocator;
+  std::unique_ptr<AppendingTypeTableBuilder> Builder;
+  std::unique_ptr<BinaryItemStream<CVType>> ItemStream;
+  std::unique_ptr<BinaryStreamReader> ReaderPrecomp;
+  std::vector<CVType> TypeArray;
+  CVTypeArray TypeStream;
+  CVTypeArray CVTypesPrecomp;
+
+  // Persistance data when loading an executable file.
+  std::unique_ptr<MemoryBuffer> BinaryBuffer;
+  std::unique_ptr<llvm::object::Binary> BinaryExecutable;
+
+  Error loadTargetInfo(const object::ObjectFile &Obj);
+  Error loadTargetInfo(const llvm::pdb::PDBFile &Pdb);
+
+  void mapRangeAddress(const object::ObjectFile &Obj,
+                       const object::SectionRef &Section,
+                       bool IsComdat) override;
+
+  llvm::object::COFFObjectFile &getObj() { return Input.obj(); }
+  llvm::pdb::PDBFile &getPdb() { return Input.pdb(); }
+  bool isObj() const { return Input.isObj(); }
+  bool isPdb() const { return Input.isPdb(); }
+  StringRef getFileName() { return Input.getFilePath(); }
+
+  // Pathname to executable image.
+  std::string ExePath;
+
+  LVOffset CurrentOffset = 0;
+  int32_t CurrentModule = -1;
+
+  using RelocMapTy = DenseMap<const llvm::object::coff_section *,
+                              std::vector<llvm::object::RelocationRef>>;
+  RelocMapTy RelocMap;
+
+  // Object files have only one type stream that contains both types and ids.
+  // Precompiled header objects don't contain an IPI stream. Use the TPI.
+  LazyRandomTypeCollection &types() {
+    return TypeServer ? TypeServer->types()
+                      : (PrecompHeader ? *PrecompHeader : Input.types());
+  }
+  LazyRandomTypeCollection &ids() {
+    return TypeServer ? TypeServer->ids()
+                      : (PrecompHeader ? *PrecompHeader : Input.ids());
+  }
+
+  LVLogicalVisitor LogicalVisitor;
+
+  Expected<StringRef>
+  getFileNameForFileOffset(uint32_t FileOffset,
+                           const llvm::pdb::SymbolGroup *SG = nullptr);
+  void printRelocatedField(StringRef Label,
+                           const llvm::object::coff_section *CoffSection,
+                           uint32_t RelocOffset, uint32_t Offset,
+                           StringRef *RelocSym);
+
+  Error printFileNameForOffset(StringRef Label, uint32_t FileOffset,
+                               const llvm::pdb::SymbolGroup *SG = nullptr);
+
+  Error loadPrecompiledObject(PrecompRecord &Precomp, CVTypeArray &CVTypesObj);
+  Error loadTypeServer(TypeServer2Record &TS);
+  Error traverseTypes(llvm::pdb::PDBFile &Pdb, LazyRandomTypeCollection &Types,
+                      LazyRandomTypeCollection &Ids);
+
+  Error collectInlineeInfo(DebugInlineeLinesSubsectionRef &Lines,
+                           const llvm::pdb::SymbolGroup *SG = nullptr);
+
+  void cacheRelocations();
+  Error resolveSymbol(const llvm::object::coff_section *CoffSection,
+                      uint64_t Offset, llvm::object::SymbolRef &Sym);
+  Error resolveSymbolName(const llvm::object::coff_section *CoffSection,
+                          uint64_t Offset, StringRef &Name);
+  Error traverseTypeSection(StringRef SectionName,
+                            const llvm::object::SectionRef &Section);
+  Error traverseSymbolSection(StringRef SectionName,
+                              const llvm::object::SectionRef &Section);
+  Error traverseInlineeLines(StringRef Subsection);
+
+  DebugChecksumsSubsectionRef CVFileChecksumTable;
+  DebugStringTableSubsectionRef CVStringTable;
+
+  Error traverseSymbolsSubsection(StringRef Subsection,
+                                  const llvm::object::SectionRef &Section,
+                                  StringRef SectionContents);
+
+  /// Given a .debug$S section, find the string table and file checksum table.
+  /// This function taken from (COFFDumper.cpp).
+  /// TODO: It can be moved to the COFF library.
+  Error initializeFileAndStringTables(BinaryStreamReader &Reader);
+
+  Error createLines(const FixedStreamArray<LineNumberEntry> &LineNumbers,
+                    LVAddress Addendum, uint32_t Segment, uint32_t Begin,
+                    uint32_t Size, uint32_t NameIndex,
+                    const llvm::pdb::SymbolGroup *SG = nullptr);
+  Error createScopes(llvm::object::COFFObjectFile &Obj);
+  Error createScopes(llvm::pdb::PDBFile &Pdb);
+  Error processModule();
+
+protected:
+  Error createScopes() override;
+  void sortScopes() override;
+
+public:
+  LVCodeViewReader() = delete;
+  LVCodeViewReader(StringRef Filename, StringRef FileFormatName,
+                   llvm::object::COFFObjectFile &Obj, ScopedPrinter &W,
+                   StringRef ExePath)
+      : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF),
+        Input(&Obj), ExePath(ExePath), LogicalVisitor(this, W, Input) {}
+  LVCodeViewReader(StringRef Filename, StringRef FileFormatName,
+                   llvm::pdb::PDBFile &Pdb, ScopedPrinter &W, StringRef ExePath)
+      : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF),
+        Input(&Pdb), ExePath(ExePath), LogicalVisitor(this, W, Input) {}
+  LVCodeViewReader(const LVCodeViewReader &) = delete;
+  LVCodeViewReader &operator=(const LVCodeViewReader &) = delete;
+  ~LVCodeViewReader() = default;
+
+  void getLinkageName(const llvm::object::coff_section *CoffSection,
+                      uint32_t RelocOffset, uint32_t Offset,
+                      StringRef *RelocSym);
+
+  void addModule(LVScope *Scope) { Modules.push_back(Scope); }
+  LVScope *getScopeForModule(uint32_t Modi) {
+    return Modi >= Modules.size() ? nullptr : Modules[Modi];
+  }
+
+  // Get the string representation for the CodeView symbols.
+  static StringRef getSymbolKindName(SymbolKind Kind);
+  static std::string formatRegisterId(RegisterId Register, CPUType CPU);
+
+  std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) override;
+
+  bool isSystemEntry(LVElement *Element, StringRef Name) const override;
+
+  void print(raw_ostream &OS) const;
+  void printRecords(raw_ostream &OS) const override {
+    LogicalVisitor.printRecords(OS);
+  };
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+  void dump() const { print(dbgs()); }
+#endif
+};
+
+} // end namespace logicalview
+} // end namespace llvm
+
+#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H

diff  --git a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h
new file mode 100644
index 0000000000000..3c461fd9e1e92
--- /dev/null
+++ b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h
@@ -0,0 +1,477 @@
+//===-- LVCodeViewVisitor.h -------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LVCodeViewVisitor class, which is used to describe a
+// debug information (CodeView) visitor.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H
+#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h"
+#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h"
+#include "llvm/DebugInfo/PDB/Native/InputFile.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+#include <stack>
+#include <utility>
+
+namespace llvm {
+namespace logicalview {
+
+using namespace llvm::codeview;
+
+class LVCodeViewReader;
+class LVLogicalVisitor;
+struct LVShared;
+
+class LVTypeVisitor final : public TypeVisitorCallbacks {
+  ScopedPrinter &W;
+  LVLogicalVisitor *LogicalVisitor;
+  LazyRandomTypeCollection &Types;
+  LazyRandomTypeCollection &Ids;
+  uint32_t StreamIdx;
+  LVShared *Shared = nullptr;
+
+  // In a PDB, a type index may refer to a type (TPI) or an item ID (IPI).
+  // In a COFF or PDB (/Z7), the type index always refer to a type (TPI).
+  // When creating logical elements, we must access the correct element
+  // table, while searching for a type index.
+  bool HasIds = false;
+
+  // Current type index during the types traversal.
+  TypeIndex CurrentTypeIndex = TypeIndex::None();
+
+  void printTypeIndex(StringRef FieldName, TypeIndex TI,
+                      uint32_t StreamIdx) const;
+
+public:
+  LVTypeVisitor(ScopedPrinter &W, LVLogicalVisitor *LogicalVisitor,
+                LazyRandomTypeCollection &Types, LazyRandomTypeCollection &Ids,
+                uint32_t StreamIdx, LVShared *Shared)
+      : TypeVisitorCallbacks(), W(W), LogicalVisitor(LogicalVisitor),
+        Types(Types), Ids(Ids), StreamIdx(StreamIdx), Shared(Shared) {
+    HasIds = &Types != &Ids;
+  }
+
+  Error visitTypeBegin(CVType &Record) override;
+  Error visitTypeBegin(CVType &Record, TypeIndex TI) override;
+  Error visitMemberBegin(CVMemberRecord &Record) override;
+  Error visitMemberEnd(CVMemberRecord &Record) override;
+  Error visitUnknownMember(CVMemberRecord &Record) override;
+
+  Error visitKnownRecord(CVType &Record, BuildInfoRecord &Args) override;
+  Error visitKnownRecord(CVType &Record, ClassRecord &Class) override;
+  Error visitKnownRecord(CVType &Record, EnumRecord &Enum) override;
+  Error visitKnownRecord(CVType &Record, FuncIdRecord &Func) override;
+  Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc) override;
+  Error visitKnownRecord(CVType &Record, StringIdRecord &String) override;
+  Error visitKnownRecord(CVType &Record, UdtSourceLineRecord &Line) override;
+  Error visitKnownRecord(CVType &Record, UnionRecord &Union) override;
+  Error visitUnknownType(CVType &Record) override;
+};
+
+class LVSymbolVisitorDelegate final : public SymbolVisitorDelegate {
+  LVCodeViewReader *Reader;
+  const llvm::object::coff_section *CoffSection;
+  StringRef SectionContents;
+
+public:
+  LVSymbolVisitorDelegate(LVCodeViewReader *Reader,
+                          const llvm::object::SectionRef &Section,
+                          const llvm::object::COFFObjectFile *Obj,
+                          StringRef SectionContents)
+      : Reader(Reader), SectionContents(SectionContents) {
+    CoffSection = Obj->getCOFFSection(Section);
+  }
+
+  uint32_t getRecordOffset(BinaryStreamReader Reader) override {
+    ArrayRef<uint8_t> Data;
+    if (Error Err = Reader.readLongestContiguousChunk(Data)) {
+      llvm::consumeError(std::move(Err));
+      return 0;
+    }
+    return Data.data() - SectionContents.bytes_begin();
+  }
+
+  void printRelocatedField(StringRef Label, uint32_t RelocOffset,
+                           uint32_t Offset, StringRef *RelocSym = nullptr);
+
+  void getLinkageName(uint32_t RelocOffset, uint32_t Offset,
+                      StringRef *RelocSym = nullptr);
+
+  StringRef getFileNameForFileOffset(uint32_t FileOffset) override;
+  DebugStringTableSubsectionRef getStringTable() override;
+};
+
+class LVElement;
+class LVScope;
+class LVSymbol;
+class LVType;
+
+// Visitor for CodeView symbol streams found in COFF object files and PDB files.
+class LVSymbolVisitor final : public SymbolVisitorCallbacks {
+  LVCodeViewReader *Reader;
+  ScopedPrinter &W;
+  LVLogicalVisitor *LogicalVisitor;
+  LazyRandomTypeCollection &Types;
+  LazyRandomTypeCollection &Ids;
+  LVSymbolVisitorDelegate *ObjDelegate;
+  LVShared *Shared;
+
+  // Symbol offset when processing PDB streams.
+  uint32_t CurrentOffset = 0;
+  // Current object name collected from S_OBJNAME.
+  StringRef CurrentObjectName;
+  // Last symbol processed by S_LOCAL.
+  LVSymbol *LocalSymbol = nullptr;
+
+  bool HasIds;
+  bool InFunctionScope = false;
+  bool IsCompileUnit = false;
+
+  // Register for the locals and parameters symbols in the current frame.
+  RegisterId LocalFrameRegister = RegisterId::NONE;
+  RegisterId ParamFrameRegister = RegisterId::NONE;
+
+  void printLocalVariableAddrRange(const LocalVariableAddrRange &Range,
+                                   uint32_t RelocationOffset);
+  void printLocalVariableAddrGap(ArrayRef<LocalVariableAddrGap> Gaps);
+  void printTypeIndex(StringRef FieldName, TypeIndex TI) const;
+
+  // Return true if this symbol is a Compile Unit.
+  bool symbolIsCompileUnit(SymbolKind Kind) {
+    switch (Kind) {
+    case SymbolKind::S_COMPILE2:
+    case SymbolKind::S_COMPILE3:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  // Determine symbol kind (local or parameter).
+  void determineSymbolKind(LVSymbol *Symbol, RegisterId Register) {
+    if (Register == LocalFrameRegister) {
+      Symbol->setIsVariable();
+      return;
+    }
+    if (Register == ParamFrameRegister) {
+      Symbol->setIsParameter();
+      return;
+    }
+    // Assume is a variable.
+    Symbol->setIsVariable();
+  }
+
+public:
+  LVSymbolVisitor(LVCodeViewReader *Reader, ScopedPrinter &W,
+                  LVLogicalVisitor *LogicalVisitor,
+                  LazyRandomTypeCollection &Types,
+                  LazyRandomTypeCollection &Ids,
+                  LVSymbolVisitorDelegate *ObjDelegate, LVShared *Shared)
+      : Reader(Reader), W(W), LogicalVisitor(LogicalVisitor), Types(Types),
+        Ids(Ids), ObjDelegate(ObjDelegate), Shared(Shared) {
+    HasIds = &Types != &Ids;
+  }
+
+  Error visitSymbolBegin(CVSymbol &Record) override;
+  Error visitSymbolBegin(CVSymbol &Record, uint32_t Offset) override;
+  Error visitSymbolEnd(CVSymbol &Record) override;
+  Error visitUnknownSymbol(CVSymbol &Record) override;
+
+  Error visitKnownRecord(CVSymbol &Record, BlockSym &Block) override;
+  Error visitKnownRecord(CVSymbol &Record, BPRelativeSym &Local) override;
+  Error visitKnownRecord(CVSymbol &Record, BuildInfoSym &BuildInfo) override;
+  Error visitKnownRecord(CVSymbol &Record, Compile2Sym &Compile2) override;
+  Error visitKnownRecord(CVSymbol &Record, Compile3Sym &Compile3) override;
+  Error visitKnownRecord(CVSymbol &Record, ConstantSym &Constant) override;
+  Error visitKnownRecord(CVSymbol &Record, DataSym &Data) override;
+  Error visitKnownRecord(CVSymbol &Record,
+                         DefRangeFramePointerRelFullScopeSym
+                             &DefRangeFramePointerRelFullScope) override;
+  Error visitKnownRecord(
+      CVSymbol &Record,
+      DefRangeFramePointerRelSym &DefRangeFramePointerRel) override;
+  Error visitKnownRecord(CVSymbol &Record,
+                         DefRangeRegisterRelSym &DefRangeRegisterRel) override;
+  Error visitKnownRecord(CVSymbol &Record,
+                         DefRangeRegisterSym &DefRangeRegister) override;
+  Error visitKnownRecord(
+      CVSymbol &Record,
+      DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) override;
+  Error visitKnownRecord(CVSymbol &Record,
+                         DefRangeSubfieldSym &DefRangeSubfield) override;
+  Error visitKnownRecord(CVSymbol &Record, DefRangeSym &DefRange) override;
+  Error visitKnownRecord(CVSymbol &Record, FrameProcSym &FrameProc) override;
+  Error visitKnownRecord(CVSymbol &Record, InlineSiteSym &InlineSite) override;
+  Error visitKnownRecord(CVSymbol &Record, LocalSym &Local) override;
+  Error visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) override;
+  Error visitKnownRecord(CVSymbol &Record, ProcSym &Proc) override;
+  Error visitKnownRecord(CVSymbol &Record, RegRelativeSym &Local) override;
+  Error visitKnownRecord(CVSymbol &Record, ScopeEndSym &ScopeEnd) override;
+  Error visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) override;
+  Error visitKnownRecord(CVSymbol &Record, UDTSym &UDT) override;
+  Error visitKnownRecord(CVSymbol &Record, UsingNamespaceSym &UN) override;
+};
+
+// Visitor for CodeView types and symbols to populate elements.
+class LVLogicalVisitor final {
+  LVCodeViewReader *Reader;
+  ScopedPrinter &W;
+
+  // Encapsulates access to the input file and any dependent type server,
+  // including any precompiled header object.
+  llvm::pdb::InputFile &Input;
+  std::shared_ptr<llvm::pdb::InputFile> TypeServer = nullptr;
+  std::shared_ptr<LazyRandomTypeCollection> PrecompHeader = nullptr;
+
+  std::shared_ptr<LVShared> Shared;
+
+  // Object files have only one type stream that contains both types and ids.
+  // Precompiled header objects don't contain an IPI stream. Use the TPI.
+  LazyRandomTypeCollection &types() {
+    return TypeServer ? TypeServer->types()
+                      : (PrecompHeader ? *PrecompHeader : Input.types());
+  }
+  LazyRandomTypeCollection &ids() {
+    return TypeServer ? TypeServer->ids()
+                      : (PrecompHeader ? *PrecompHeader : Input.ids());
+  }
+
+  using LVScopeStack = std::stack<LVScope *>;
+  LVScopeStack ScopeStack;
+  LVScope *ReaderParent = nullptr;
+  LVScope *ReaderScope = nullptr;
+  bool InCompileUnitScope = false;
+
+  // Allow processing of argument list.
+  bool ProcessArgumentList = false;
+  StringRef OverloadedMethodName;
+  std::string CompileUnitName;
+
+  // Inlined functions source information.
+  using LVInlineeEntry = std::pair<uint32_t, StringRef>;
+  using LVInlineeInfo = std::map<TypeIndex, LVInlineeEntry>;
+  LVInlineeInfo InlineeInfo;
+
+  Error visitFieldListMemberStream(TypeIndex TI, LVElement *Element,
+                                   ArrayRef<uint8_t> FieldList);
+
+  LVType *createBaseType(TypeIndex TI, StringRef TypeName);
+  LVType *createPointerType(TypeIndex TI, StringRef TypeName);
+  LVSymbol *createParameter(TypeIndex TI, StringRef Name, LVScope *Parent);
+  LVSymbol *createParameter(LVElement *Element, StringRef Name,
+                            LVScope *Parent);
+  void createDataMember(CVMemberRecord &Record, LVScope *Parent, StringRef Name,
+                        TypeIndex Type, MemberAccess Access);
+  void createParents(StringRef ScopedName, LVElement *Element);
+
+public:
+  LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W,
+                   llvm::pdb::InputFile &Input);
+
+  // Current elements during the processing of a RecordType or RecordSymbol.
+  // They are shared with the SymbolVisitor.
+  LVElement *CurrentElement = nullptr;
+  LVScope *CurrentScope = nullptr;
+  LVSymbol *CurrentSymbol = nullptr;
+  LVType *CurrentType = nullptr;
+
+  // Input source in the case of type server or precompiled header.
+  void setInput(std::shared_ptr<llvm::pdb::InputFile> TypeServer) {
+    this->TypeServer = TypeServer;
+  }
+  void setInput(std::shared_ptr<LazyRandomTypeCollection> PrecompHeader) {
+    this->PrecompHeader = PrecompHeader;
+  }
+
+  void addInlineeInfo(TypeIndex TI, uint32_t LineNumber, StringRef Filename) {
+    InlineeInfo.emplace(std::piecewise_construct, std::forward_as_tuple(TI),
+                        std::forward_as_tuple(LineNumber, Filename));
+  }
+
+  void printTypeIndex(StringRef FieldName, TypeIndex TI, uint32_t StreamIdx);
+  void printMemberAttributes(MemberAttributes Attrs);
+  void printMemberAttributes(MemberAccess Access, MethodKind Kind,
+                             MethodOptions Options);
+
+  LVElement *createElement(TypeLeafKind Kind);
+  LVElement *createElement(SymbolKind Kind);
+  LVElement *createElement(TypeIndex TI, TypeLeafKind Kind);
+
+  // Break down the annotation byte code and calculate code and line offsets.
+  Error inlineSiteAnnotation(LVScope *AbstractFunction,
+                             LVScope *InlinedFunction,
+                             InlineSiteSym &InlineSite);
+
+  void pushScope(LVScope *Scope) {
+    ScopeStack.push(ReaderParent);
+    ReaderParent = ReaderScope;
+    ReaderScope = Scope;
+  }
+  void popScope() {
+    ReaderScope = ReaderParent;
+    ReaderParent = ScopeStack.top();
+    ScopeStack.pop();
+  }
+  void closeScope() {
+    if (InCompileUnitScope) {
+      InCompileUnitScope = false;
+      popScope();
+    }
+  }
+  void setRoot(LVScope *Root) { ReaderScope = Root; }
+
+  void addElement(LVScope *Scope, bool IsCompileUnit);
+  void addElement(LVSymbol *Symbol);
+  void addElement(LVType *Type);
+
+  std::string getCompileUnitName() { return CompileUnitName; }
+  void setCompileUnitName(std::string Name) {
+    CompileUnitName = std::move(Name);
+  }
+
+  LVElement *getElement(uint32_t StreamIdx, TypeIndex TI,
+                        LVScope *Parent = nullptr);
+  LVShared *getShared() { return Shared.get(); }
+
+  LVScope *getReaderScope() const { return ReaderScope; }
+
+  void printTypeBegin(CVType &Record, TypeIndex TI, LVElement *Element,
+                      uint32_t StreamIdx);
+  void printTypeEnd(CVType &Record);
+  void printMemberBegin(CVMemberRecord &Record, TypeIndex TI,
+                        LVElement *Element, uint32_t StreamIdx);
+  void printMemberEnd(CVMemberRecord &Record);
+
+  void startProcessArgumentList() { ProcessArgumentList = true; }
+  void stopProcessArgumentList() { ProcessArgumentList = false; }
+
+  void processFiles();
+  void processLines();
+  void processNamespaces();
+
+  void printRecords(raw_ostream &OS) const;
+
+  Error visitUnknownType(CVType &Record, TypeIndex TI);
+  Error visitKnownRecord(CVType &Record, ArgListRecord &Args, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, ArrayRecord &AT, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, BitFieldRecord &BF, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, BuildInfoRecord &BI, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, ClassRecord &Class, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, EnumRecord &Enum, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, FieldListRecord &FieldList,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownRecord(CVType &Record, FuncIdRecord &Func, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, LabelRecord &LR, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, ModifierRecord &Mod, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, MemberFunctionRecord &MF, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, MethodOverloadListRecord &Overloads,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownRecord(CVType &Record, PointerRecord &Ptr, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, UnionRecord &Union, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, TypeServer2Record &TS, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, VFTableRecord &VFT, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, VFTableShapeRecord &Shape,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownRecord(CVType &Record, StringListRecord &Strings,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownRecord(CVType &Record, StringIdRecord &String, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, UdtSourceLineRecord &SourceLine,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownRecord(CVType &Record, UdtModSourceLineRecord &ModSourceLine,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownRecord(CVType &Record, PrecompRecord &Precomp, TypeIndex TI,
+                         LVElement *Element);
+  Error visitKnownRecord(CVType &Record, EndPrecompRecord &EndPrecomp,
+                         TypeIndex TI, LVElement *Element);
+
+  Error visitUnknownMember(CVMemberRecord &Record, TypeIndex TI);
+  Error visitKnownMember(CVMemberRecord &Record, BaseClassRecord &Base,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, DataMemberRecord &Field,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, EnumeratorRecord &Enum,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, ListContinuationRecord &Cont,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, NestedTypeRecord &Nested,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, OneMethodRecord &Method,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, OverloadedMethodRecord &Method,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, StaticDataMemberRecord &Field,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, VFPtrRecord &VFTable,
+                         TypeIndex TI, LVElement *Element);
+  Error visitKnownMember(CVMemberRecord &Record, VirtualBaseClassRecord &Base,
+                         TypeIndex TI, LVElement *Element);
+
+  template <typename T>
+  Error visitKnownMember(CVMemberRecord &Record,
+                         TypeVisitorCallbacks &Callbacks, TypeIndex TI,
+                         LVElement *Element) {
+    TypeRecordKind RK = static_cast<TypeRecordKind>(Record.Kind);
+    T KnownRecord(RK);
+    if (Error Err = Callbacks.visitKnownMember(Record, KnownRecord))
+      return Err;
+    if (Error Err = visitKnownMember(Record, KnownRecord, TI, Element))
+      return Err;
+    return Error::success();
+  }
+
+  template <typename T>
+  Error visitKnownRecord(CVType &Record, TypeIndex TI, LVElement *Element) {
+    TypeRecordKind RK = static_cast<TypeRecordKind>(Record.kind());
+    T KnownRecord(RK);
+    if (Error Err = TypeDeserializer::deserializeAs(
+            const_cast<CVType &>(Record), KnownRecord))
+      return Err;
+    if (Error Err = visitKnownRecord(Record, KnownRecord, TI, Element))
+      return Err;
+    return Error::success();
+  }
+
+  Error visitMemberRecord(CVMemberRecord &Record,
+                          TypeVisitorCallbacks &Callbacks, TypeIndex TI,
+                          LVElement *Element);
+  Error finishVisitation(CVType &Record, TypeIndex TI, LVElement *Element);
+};
+
+} // namespace logicalview
+} // namespace llvm
+
+#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H

diff  --git a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt
index 3388c25230394..38a174661b4f3 100644
--- a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt
+++ b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt
@@ -22,6 +22,8 @@ add_lv_impl_folder(Core
 add_lv_impl_folder(Readers
   LVReaderHandler.cpp
   Readers/LVBinaryReader.cpp
+  Readers/LVCodeViewReader.cpp
+  Readers/LVCodeViewVisitor.cpp
   Readers/LVELFReader.cpp
   )
 
@@ -39,9 +41,12 @@ add_llvm_component_library(LLVMDebugInfoLogicalView
 
   LINK_COMPONENTS
   BinaryFormat
+  Demangle
   Object
   MC
   Support
   TargetParser
   DebugInfoDWARF
+  DebugInfoCodeView
+  DebugInfoPDB
   )

diff  --git a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
index a320752befc46..cfe304eead512 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
@@ -17,6 +17,7 @@
 #include "llvm/DebugInfo/LogicalView/Core/LVType.h"
 
 using namespace llvm;
+using namespace llvm::codeview;
 using namespace llvm::logicalview;
 
 #define DEBUG_TYPE "Element"
@@ -103,6 +104,14 @@ void LVElement::setFilename(StringRef Filename) {
   FilenameIndex = getStringIndex(Filename);
 }
 
+void LVElement::setInnerComponent(StringRef Name) {
+  if (Name.size()) {
+    StringRef InnerComponent;
+    std::tie(std::ignore, InnerComponent) = getInnerComponent(Name);
+    setName(InnerComponent);
+  }
+}
+
 // Return the string representation of a DIE offset.
 std::string LVElement::typeOffsetAsString() const {
   if (options().getAttributeOffset()) {
@@ -126,6 +135,19 @@ StringRef LVElement::accessibilityString(uint32_t Access) const {
   }
 }
 
+std::optional<uint32_t> LVElement::getAccessibilityCode(MemberAccess Access) {
+  switch (Access) {
+  case MemberAccess::Private:
+    return dwarf::DW_ACCESS_private;
+  case MemberAccess::Protected:
+    return dwarf::DW_ACCESS_protected;
+  case MemberAccess::Public:
+    return dwarf::DW_ACCESS_public;
+  default:
+    return std::nullopt;
+  }
+}
+
 StringRef LVElement::externalString() const {
   return getIsExternal() ? "extern" : StringRef();
 }
@@ -160,6 +182,21 @@ StringRef LVElement::virtualityString(uint32_t Virtuality) const {
   }
 }
 
+std::optional<uint32_t> LVElement::getVirtualityCode(MethodKind Virtuality) {
+  switch (Virtuality) {
+  case MethodKind::Virtual:
+    return dwarf::DW_VIRTUALITY_virtual;
+  case MethodKind::PureVirtual:
+    return dwarf::DW_VIRTUALITY_pure_virtual;
+  case MethodKind::IntroducingVirtual:
+  case MethodKind::PureIntroducingVirtual:
+    // No direct equivalents in DWARF. Assume Virtual.
+    return dwarf::DW_VIRTUALITY_virtual;
+  default:
+    return std::nullopt;
+  }
+}
+
 void LVElement::resolve() {
   if (getIsResolved())
     return;

diff  --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
index 88f66cf2093be..613452c0b501c 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -182,6 +182,9 @@ Error LVReader::createSplitFolder() {
 
 // Get the filename for given object.
 StringRef LVReader::getFilename(LVObject *Object, size_t Index) const {
+  // TODO: The current CodeView Reader implementation does not have support
+  // for multiple compile units. Until we have a proper offset calculation,
+  // check only in the current compile unit.
   if (CompileUnits.size()) {
     // Get Compile Unit for the given object.
     LVCompileUnits::const_iterator Iter =

diff  --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
index 2fc97ccb6690b..2f26025d01ec2 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
@@ -589,6 +589,10 @@ Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS,
   // split context, then switch to the reader output stream.
   raw_ostream *StreamSplit = &OS;
 
+  // Ignore the CU generated by the VS toolchain, when compiling to PDB.
+  if (getIsSystem() && !options().getAttributeSystem())
+    return Error::success();
+
   // If 'Split', we use the scope name (CU name) as the ouput file; the
   // delimiters in the pathname, must be replaced by a normal character.
   if (getIsCompileUnit()) {
@@ -1784,6 +1788,8 @@ void LVScopeFunction::resolveReferences() {
   //	            DW_AT_external DW_FORM_flag_present
   // 00000070 DW_TAG_subprogram "bar"
   //   DW_AT_specification DW_FORM_ref4 0x00000048
+  // CodeView does not include any information at the class level to
+  // mark the member function as external.
   // If there is a reference linking the declaration and definition, mark
   // the definition as extern, to facilitate the logical view comparison.
   if (getHasReferenceSpecification()) {
@@ -2019,6 +2025,28 @@ void LVScopeRoot::processRangeInformation() {
     }
 }
 
+void LVScopeRoot::transformScopedName() {
+  // Recursively transform all names.
+  std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) {
+    auto Traverse = [&](const auto *Set) {
+      if (Set)
+        for (const auto &Entry : *Set)
+          Entry->setInnerComponent();
+    };
+    if (const LVScopes *Scopes = Parent->getScopes())
+      for (LVScope *Scope : *Scopes) {
+        Scope->setInnerComponent();
+        TraverseScope(Scope);
+      }
+    Traverse(Parent->getSymbols());
+    Traverse(Parent->getTypes());
+    Traverse(Parent->getLines());
+  };
+
+  // Start traversing the scopes root and transform the element name.
+  TraverseScope(this);
+}
+
 bool LVScopeRoot::equals(const LVScope *Scope) const {
   return LVScope::equals(Scope);
 }

diff  --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp
index 6d55b755ed46c..42fb1142eb441 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp
@@ -60,3 +60,106 @@ std::string llvm::logicalview::flattenedFilePath(StringRef Path) {
   };
   return Name;
 }
+
+using LexicalEntry = std::pair<size_t, size_t>;
+using LexicalIndexes = SmallVector<LexicalEntry, 10>;
+
+static LexicalIndexes getAllLexicalIndexes(StringRef Name) {
+  if (Name.empty())
+    return {};
+
+  size_t AngleCount = 0;
+  size_t ColonSeen = 0;
+  size_t Current = 0;
+
+  LexicalIndexes Indexes;
+
+#ifndef NDEBUG
+  auto PrintLexicalEntry = [&]() {
+    LexicalEntry Entry = Indexes.back();
+    llvm::dbgs() << formatv(
+        "'{0}:{1}', '{2}'\n", Entry.first, Entry.second,
+        Name.substr(Entry.first, Entry.second - Entry.first + 1));
+  };
+#endif
+
+  size_t Length = Name.size();
+  for (size_t Index = 0; Index < Length; ++Index) {
+    LLVM_DEBUG({
+      llvm::dbgs() << formatv("Index: '{0}', Char: '{1}'\n", Index,
+                              Name[Index]);
+    });
+    switch (Name[Index]) {
+    case '<':
+      ++AngleCount;
+      break;
+    case '>':
+      --AngleCount;
+      break;
+    case ':':
+      ++ColonSeen;
+      break;
+    }
+    if (ColonSeen == 2) {
+      if (!AngleCount) {
+        Indexes.push_back(LexicalEntry(Current, Index - 2));
+        Current = Index + 1;
+        LLVM_DEBUG({ PrintLexicalEntry(); });
+      }
+      ColonSeen = 0;
+      continue;
+    }
+  }
+
+  // Store last component.
+  Indexes.push_back(LexicalEntry(Current, Length - 1));
+  LLVM_DEBUG({ PrintLexicalEntry(); });
+  return Indexes;
+}
+
+LVLexicalComponent llvm::logicalview::getInnerComponent(StringRef Name) {
+  if (Name.empty())
+    return {};
+
+  LexicalIndexes Indexes = getAllLexicalIndexes(Name);
+  if (Indexes.size() == 1)
+    return std::make_tuple(StringRef(), Name);
+
+  LexicalEntry BeginEntry = Indexes.front();
+  LexicalEntry EndEntry = Indexes[Indexes.size() - 2];
+  StringRef Outer =
+      Name.substr(BeginEntry.first, EndEntry.second - BeginEntry.first + 1);
+
+  LexicalEntry LastEntry = Indexes.back();
+  StringRef Inner =
+      Name.substr(LastEntry.first, LastEntry.second - LastEntry.first + 1);
+
+  return std::make_tuple(Outer, Inner);
+}
+
+LVStringRefs llvm::logicalview::getAllLexicalComponents(StringRef Name) {
+  if (Name.empty())
+    return {};
+
+  LexicalIndexes Indexes = getAllLexicalIndexes(Name);
+  LVStringRefs Components;
+  for (const LexicalEntry &Entry : Indexes)
+    Components.push_back(
+        Name.substr(Entry.first, Entry.second - Entry.first + 1));
+
+  return Components;
+}
+
+std::string llvm::logicalview::getScopedName(const LVStringRefs &Components,
+                                             StringRef BaseName) {
+  if (Components.empty())
+    return {};
+  std::string Name(BaseName);
+  raw_string_ostream Stream(Name);
+  if (BaseName.size())
+    Stream << "::";
+  Stream << Components[0];
+  for (LVStringRefs::size_type Index = 1; Index < Components.size(); ++Index)
+    Stream << "::" << Components[Index];
+  return Name;
+}

diff  --git a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp
index de71e0904dbbc..28bccadce598c 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp
@@ -324,6 +324,13 @@ LVElement *LVTypeDefinition::getUnderlyingType() {
 }
 
 void LVTypeDefinition::resolveExtra() {
+  // In the case of CodeView, the MSVC toolset generates a series of typedefs
+  // that refer to internal runtime structures, that we do not process. Those
+  // typedefs are marked as 'system'. They have an associated logical type,
+  // but the underlying type always is null.
+  if (getIsSystem())
+    return;
+
   // Set the reference to the typedef type.
   if (options().getAttributeUnderlying()) {
     setUnderlyingType(getUnderlyingType());

diff  --git a/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp b/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp
index 65633f2495f42..4c27dfe7e9bc6 100644
--- a/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp
@@ -11,8 +11,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
 #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h"
 #include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Object/COFF.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -38,9 +43,19 @@ Error LVReaderHandler::createReader(StringRef Filename, LVReaders &Readers,
   auto CreateOneReader = [&]() -> std::unique_ptr<LVReader> {
     if (Input.is<ObjectFile *>()) {
       ObjectFile &Obj = *Input.get<ObjectFile *>();
+      if (Obj.isCOFF()) {
+        COFFObjectFile *COFF = cast<COFFObjectFile>(&Obj);
+        return std::make_unique<LVCodeViewReader>(Filename, FileFormatName,
+                                                  *COFF, W, ExePath);
+      }
       if (Obj.isELF() || Obj.isMachO())
         return std::make_unique<LVELFReader>(Filename, FileFormatName, Obj, W);
     }
+    if (Input.is<PDBFile *>()) {
+      PDBFile &Pdb = *Input.get<PDBFile *>();
+      return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, Pdb,
+                                                W, ExePath);
+    }
     return nullptr;
   };
 
@@ -76,8 +91,102 @@ Error LVReaderHandler::handleArchive(LVReaders &Readers, StringRef Filename,
   return Error::success();
 }
 
+// Search for a matching executable image for the given PDB path.
+static std::string searchForExe(const StringRef Path,
+                                const StringRef Extension) {
+  SmallString<128> ExePath(Path);
+  llvm::sys::path::replace_extension(ExePath, Extension);
+
+  std::unique_ptr<IPDBSession> Session;
+  if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) {
+    consumeError(std::move(Err));
+    return {};
+  }
+  // We have a candidate for the executable image.
+  Expected<std::string> PdbPathOrErr = NativeSession::searchForPdb({ExePath});
+  if (!PdbPathOrErr) {
+    consumeError(PdbPathOrErr.takeError());
+    return {};
+  }
+  // Convert any Windows backslashes into forward slashes to get the path.
+  std::string ConvertedPath = sys::path::convert_to_slash(
+      PdbPathOrErr.get(), sys::path::Style::windows);
+  if (ConvertedPath == Path)
+    return std::string(ExePath);
+
+  return {};
+}
+
+// Search for a matching object image for the given PDB path.
+static std::string searchForObj(const StringRef Path,
+                                const StringRef Extension) {
+  SmallString<128> ObjPath(Path);
+  llvm::sys::path::replace_extension(ObjPath, Extension);
+  if (llvm::sys::fs::exists(ObjPath)) {
+    ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+        MemoryBuffer::getFileOrSTDIN(ObjPath);
+    if (!BuffOrErr)
+      return {};
+    return std::string(ObjPath);
+  }
+
+  return {};
+}
+
 Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename,
                                     MemoryBufferRef Buffer, StringRef ExePath) {
+  // As PDB does not support the Binary interface, at this point we can check
+  // if the buffer corresponds to a PDB or PE file.
+  file_magic FileMagic = identify_magic(Buffer.getBuffer());
+  if (FileMagic == file_magic::pdb) {
+    if (!ExePath.empty())
+      return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath);
+
+    // Search in the directory derived from the given 'Filename' for a
+    // matching object file (.o, .obj, .lib) or a matching executable file
+    // (.exe/.dll) and try to create the reader based on the matched file.
+    // If no matching file is found then we load the original PDB file.
+    std::vector<StringRef> ExecutableExtensions = {"exe", "dll"};
+    for (StringRef Extension : ExecutableExtensions) {
+      std::string ExecutableImage = searchForExe(Filename, Extension);
+      if (ExecutableImage.empty())
+        continue;
+      if (Error Err = handleObject(Readers, Filename, Buffer.getBuffer(),
+                                   ExecutableImage)) {
+        consumeError(std::move(Err));
+        continue;
+      }
+      return Error::success();
+    }
+
+    std::vector<StringRef> ObjectExtensions = {"o", "obj", "lib"};
+    for (StringRef Extension : ObjectExtensions) {
+      std::string ObjectImage = searchForObj(Filename, Extension);
+      if (ObjectImage.empty())
+        continue;
+      if (Error Err = handleFile(Readers, ObjectImage)) {
+        consumeError(std::move(Err));
+        continue;
+      }
+      return Error::success();
+    }
+
+    // No matching executable/object image was found. Load the given PDB.
+    return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath);
+  }
+  if (FileMagic == file_magic::pecoff_executable) {
+    // If we have a valid executable, try to find a matching PDB file.
+    Expected<std::string> PdbPath = NativeSession::searchForPdb({Filename});
+    if (errorToErrorCode(PdbPath.takeError())) {
+      return createStringError(
+          errc::not_supported,
+          "Binary object format in '%s' does not have debug info.",
+          Filename.str().c_str());
+    }
+    // Process the matching PDB file and pass the executable filename.
+    return handleFile(Readers, PdbPath.get(), Filename);
+  }
+
   Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buffer);
   if (errorToErrorCode(BinOrErr.takeError())) {
     return createStringError(errc::not_supported,
@@ -147,6 +256,23 @@ Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename,
                            Filename.str().c_str());
 }
 
+Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename,
+                                    StringRef Buffer, StringRef ExePath) {
+  std::unique_ptr<IPDBSession> Session;
+  if (Error Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session))
+    return createStringError(errorToErrorCode(std::move(Err)), "%s",
+                             Filename.str().c_str());
+
+  std::unique_ptr<NativeSession> PdbSession;
+  PdbSession.reset(static_cast<NativeSession *>(Session.release()));
+  PdbOrObj Input = &PdbSession->getPDBFile();
+  StringRef FileFormatName;
+  size_t Pos = Buffer.find_first_of("\r\n");
+  if (Pos)
+    FileFormatName = Buffer.substr(0, Pos - 1);
+  return createReader(Filename, Readers, Input, FileFormatName, ExePath);
+}
+
 Error LVReaderHandler::createReaders() {
   LLVM_DEBUG(dbgs() << "createReaders\n");
   for (std::string &Object : Objects) {

diff  --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
index 1fb5c4a9d9105..2be72a2c4e745 100644
--- a/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp
@@ -190,6 +190,61 @@ void LVBinaryReader::mapVirtualAddress(const object::ObjectFile &Obj) {
   });
 }
 
+void LVBinaryReader::mapVirtualAddress(const object::COFFObjectFile &COFFObj) {
+  ErrorOr<uint64_t> ImageBase = COFFObj.getImageBase();
+  if (ImageBase)
+    ImageBaseAddress = ImageBase.get();
+
+  LLVM_DEBUG({
+    dbgs() << "ImageBaseAddress: " << hexValue(ImageBaseAddress) << "\n";
+  });
+
+  uint32_t Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_LNK_COMDAT;
+
+  for (const object::SectionRef &Section : COFFObj.sections()) {
+    if (!Section.isText() || Section.isVirtual() || !Section.getSize())
+      continue;
+
+    const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section);
+    VirtualAddress = COFFSection->VirtualAddress;
+    bool IsComdat = (COFFSection->Characteristics & Flags) == Flags;
+
+    // Record section information required for symbol resolution.
+    // Note: The section index returned by 'getIndex()' is zero based.
+    Sections.emplace(Section.getIndex() + 1, Section);
+    addSectionAddress(Section);
+
+    // Additional initialization on the specific object format.
+    mapRangeAddress(COFFObj, Section, IsComdat);
+  }
+
+  LLVM_DEBUG({
+    dbgs() << "\nSections Information:\n";
+    for (LVSections::reference Entry : Sections) {
+      LVSectionIndex SectionIndex = Entry.first;
+      const object::SectionRef Section = Entry.second;
+      const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section);
+      Expected<StringRef> SectionNameOrErr = Section.getName();
+      if (!SectionNameOrErr)
+        consumeError(SectionNameOrErr.takeError());
+      dbgs() << "\nIndex: " << format_decimal(SectionIndex, 3)
+             << " Name: " << *SectionNameOrErr << "\n"
+             << "Size: " << hexValue(Section.getSize()) << "\n"
+             << "VirtualAddress: " << hexValue(VirtualAddress) << "\n"
+             << "SectionAddress: " << hexValue(Section.getAddress()) << "\n"
+             << "PointerToRawData: " << hexValue(COFFSection->PointerToRawData)
+             << "\n"
+             << "SizeOfRawData: " << hexValue(COFFSection->SizeOfRawData)
+             << "\n";
+    }
+    dbgs() << "\nObject Section Information:\n";
+    for (LVSectionAddresses::const_reference Entry : SectionAddresses)
+      dbgs() << "[" << hexValue(Entry.first) << ":"
+             << hexValue(Entry.first + Entry.second.getSize())
+             << "] Size: " << hexValue(Entry.second.getSize()) << "\n";
+  });
+}
+
 Error LVBinaryReader::loadGenericTargetInfo(StringRef TheTriple,
                                             StringRef TheFeatures) {
   std::string TargetLookupError;
@@ -804,6 +859,80 @@ void LVBinaryReader::processLines(LVLines *DebugLines,
   }
 }
 
+// Traverse the scopes for the given 'Function' looking for any inlined
+// scopes with inlined lines, which are found in 'CUInlineeLines'.
+void LVBinaryReader::includeInlineeLines(LVSectionIndex SectionIndex,
+                                         LVScope *Function) {
+  SmallVector<LVInlineeLine::iterator> InlineeIters;
+  std::function<void(LVScope * Parent)> FindInlinedScopes =
+      [&](LVScope *Parent) {
+        if (const LVScopes *Scopes = Parent->getScopes())
+          for (LVScope *Scope : *Scopes) {
+            LVInlineeLine::iterator Iter = CUInlineeLines.find(Scope);
+            if (Iter != CUInlineeLines.end())
+              InlineeIters.push_back(Iter);
+            FindInlinedScopes(Scope);
+          }
+      };
+
+  // Find all inlined scopes for the given 'Function'.
+  FindInlinedScopes(Function);
+  for (LVInlineeLine::iterator InlineeIter : InlineeIters) {
+    LVScope *Scope = InlineeIter->first;
+    addToSymbolTable(Scope->getLinkageName(), Scope, SectionIndex);
+
+    // TODO: Convert this into a reference.
+    LVLines *InlineeLines = InlineeIter->second.get();
+    LLVM_DEBUG({
+      dbgs() << "Inlined lines for: " << Scope->getName() << "\n";
+      for (const LVLine *Line : *InlineeLines)
+        dbgs() << "[" << hexValue(Line->getAddress()) << "] "
+               << Line->getLineNumber() << "\n";
+      dbgs() << format("Debug lines: %d\n", CULines.size());
+      for (const LVLine *Line : CULines)
+        dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", ("
+               << Line->getLineNumber() << ")\n";
+      ;
+    });
+
+    // The inlined lines must be merged using its address, in order to keep
+    // the real order of the instructions. The inlined lines are mixed with
+    // the other non-inlined lines.
+    if (InlineeLines->size()) {
+      // First address of inlinee code.
+      uint64_t InlineeStart = (InlineeLines->front())->getAddress();
+      LVLines::iterator Iter = std::find_if(
+          CULines.begin(), CULines.end(), [&](LVLine *Item) -> bool {
+            return Item->getAddress() == InlineeStart;
+          });
+      if (Iter != CULines.end()) {
+        // 'Iter' points to the line where the inlined function is called.
+        // Emulate the DW_AT_call_line attribute.
+        Scope->setCallLineNumber((*Iter)->getLineNumber());
+        // Mark the referenced line as the start of the inlined function.
+        // Skip the first line during the insertion, as the address and
+        // line number as the same. Otherwise we have to erase and insert.
+        (*Iter)->setLineNumber((*InlineeLines->begin())->getLineNumber());
+        ++Iter;
+        CULines.insert(Iter, InlineeLines->begin() + 1, InlineeLines->end());
+      }
+    }
+
+    // Remove this set of lines from the container; each inlined function
+    // creates an unique set of lines. Remove only the created container.
+    CUInlineeLines.erase(InlineeIter);
+    InlineeLines->clear();
+  }
+  LLVM_DEBUG({
+    dbgs() << "Merged Inlined lines for: " << Function->getName() << "\n";
+    dbgs() << format("Debug lines: %d\n", CULines.size());
+    for (const LVLine *Line : CULines)
+      dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", ("
+             << Line->getLineNumber() << ")\n";
+    ;
+  });
+}
+
 void LVBinaryReader::print(raw_ostream &OS) const {
   OS << "LVBinaryReader\n";
   LLVM_DEBUG(dbgs() << "PrintReader\n");

diff  --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp
new file mode 100644
index 0000000000000..5b099d47c45a4
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp
@@ -0,0 +1,1221 @@
+//===-- LVCodeViewReader.cpp ----------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVCodeViewReader class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h"
+#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/EnumTables.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
+#include "llvm/DebugInfo/PDB/GenericError.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/LinePrinter.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::logicalview;
+using namespace llvm::msf;
+using namespace llvm::object;
+using namespace llvm::pdb;
+
+#define DEBUG_TYPE "CodeViewReader"
+
+StringRef LVCodeViewReader::getSymbolKindName(SymbolKind Kind) {
+  switch (Kind) {
+#define SYMBOL_RECORD(EnumName, EnumVal, Name)                                 \
+  case EnumName:                                                               \
+    return #EnumName;
+#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def"
+  default:
+    return "UnknownSym";
+  }
+  llvm_unreachable("Unknown SymbolKind::Kind");
+}
+
+std::string LVCodeViewReader::formatRegisterId(RegisterId Register,
+                                               CPUType CPU) {
+#define RETURN_CASE(Enum, X, Ret)                                              \
+  case Enum::X:                                                                \
+    return Ret;
+
+  if (CPU == CPUType::ARMNT) {
+    switch (Register) {
+#define CV_REGISTERS_ARM
+#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
+#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
+#undef CV_REGISTER
+#undef CV_REGISTERS_ARM
+
+    default:
+      break;
+    }
+  } else if (CPU == CPUType::ARM64) {
+    switch (Register) {
+#define CV_REGISTERS_ARM64
+#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
+#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
+#undef CV_REGISTER
+#undef CV_REGISTERS_ARM64
+
+    default:
+      break;
+    }
+  } else {
+    switch (Register) {
+#define CV_REGISTERS_X86
+#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
+#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
+#undef CV_REGISTER
+#undef CV_REGISTERS_X86
+
+    default:
+      break;
+    }
+  }
+  return "formatUnknownEnum(Id)";
+}
+
+void LVCodeViewReader::printRelocatedField(StringRef Label,
+                                           const coff_section *CoffSection,
+                                           uint32_t RelocOffset,
+                                           uint32_t Offset,
+                                           StringRef *RelocSym) {
+  StringRef SymStorage;
+  StringRef &Symbol = RelocSym ? *RelocSym : SymStorage;
+  if (!resolveSymbolName(CoffSection, RelocOffset, Symbol))
+    W.printSymbolOffset(Label, Symbol, Offset);
+  else
+    W.printHex(Label, RelocOffset);
+}
+
+void LVCodeViewReader::getLinkageName(const coff_section *CoffSection,
+                                      uint32_t RelocOffset, uint32_t Offset,
+                                      StringRef *RelocSym) {
+  StringRef SymStorage;
+  StringRef &Symbol = RelocSym ? *RelocSym : SymStorage;
+  if (resolveSymbolName(CoffSection, RelocOffset, Symbol))
+    Symbol = "";
+}
+
+Expected<StringRef>
+LVCodeViewReader::getFileNameForFileOffset(uint32_t FileOffset,
+                                           const SymbolGroup *SG) {
+  if (SG) {
+    Expected<StringRef> Filename = SG->getNameFromChecksums(FileOffset);
+    if (!Filename) {
+      consumeError(Filename.takeError());
+      return StringRef("");
+    }
+    return *Filename;
+  }
+
+  // The file checksum subsection should precede all references to it.
+  if (!CVFileChecksumTable.valid() || !CVStringTable.valid())
+    return createStringError(object_error::parse_failed, getFileName());
+
+  VarStreamArray<FileChecksumEntry>::Iterator Iter =
+      CVFileChecksumTable.getArray().at(FileOffset);
+
+  // Check if the file checksum table offset is valid.
+  if (Iter == CVFileChecksumTable.end())
+    return createStringError(object_error::parse_failed, getFileName());
+
+  Expected<StringRef> NameOrErr = CVStringTable.getString(Iter->FileNameOffset);
+  if (!NameOrErr)
+    return createStringError(object_error::parse_failed, getFileName());
+  return *NameOrErr;
+}
+
+Error LVCodeViewReader::printFileNameForOffset(StringRef Label,
+                                               uint32_t FileOffset,
+                                               const SymbolGroup *SG) {
+  Expected<StringRef> NameOrErr = getFileNameForFileOffset(FileOffset, SG);
+  if (!NameOrErr)
+    return NameOrErr.takeError();
+  W.printHex(Label, *NameOrErr, FileOffset);
+  return Error::success();
+}
+
+void LVCodeViewReader::cacheRelocations() {
+  for (const SectionRef &Section : getObj().sections()) {
+    const coff_section *CoffSection = getObj().getCOFFSection(Section);
+
+    for (const RelocationRef &Relocacion : Section.relocations())
+      RelocMap[CoffSection].push_back(Relocacion);
+
+    // Sort relocations by address.
+    llvm::sort(RelocMap[CoffSection], [](RelocationRef L, RelocationRef R) {
+      return L.getOffset() < R.getOffset();
+    });
+  }
+}
+
+// Given a section and an offset into this section the function returns the
+// symbol used for the relocation at the offset.
+Error LVCodeViewReader::resolveSymbol(const coff_section *CoffSection,
+                                      uint64_t Offset, SymbolRef &Sym) {
+  const auto &Relocations = RelocMap[CoffSection];
+  basic_symbol_iterator SymI = getObj().symbol_end();
+  for (const RelocationRef &Relocation : Relocations) {
+    uint64_t RelocationOffset = Relocation.getOffset();
+
+    if (RelocationOffset == Offset) {
+      SymI = Relocation.getSymbol();
+      break;
+    }
+  }
+  if (SymI == getObj().symbol_end())
+    return make_error<StringError>("Unknown Symbol", inconvertibleErrorCode());
+  Sym = *SymI;
+  return ErrorSuccess();
+}
+
+// Given a section and an offset into this section the function returns the
+// name of the symbol used for the relocation at the offset.
+Error LVCodeViewReader::resolveSymbolName(const coff_section *CoffSection,
+                                          uint64_t Offset, StringRef &Name) {
+  SymbolRef Symbol;
+  if (Error E = resolveSymbol(CoffSection, Offset, Symbol))
+    return E;
+  Expected<StringRef> NameOrErr = Symbol.getName();
+  if (!NameOrErr)
+    return NameOrErr.takeError();
+  Name = *NameOrErr;
+  return ErrorSuccess();
+}
+
+// CodeView and DWARF can have references to compiler generated elements,
+// used for initialization. The MSVC includes in the PDBs, internal compile
+// units, associated with the MS runtime support. We mark them as 'system'
+// and they are printed only if the command line option 'internal=system'.
+bool LVCodeViewReader::isSystemEntry(LVElement *Element, StringRef Name) const {
+  Name = Name.empty() ? Element->getName() : Name;
+  auto Find = [=](const char *String) -> bool {
+    return StringRef::npos != Name.find(String);
+  };
+  auto Starts = [=](const char *Pattern) -> bool {
+    return Name.startswith(Pattern);
+  };
+  auto CheckExclude = [&]() -> bool {
+    if (Starts("__") || Starts("_PMD") || Starts("_PMFN"))
+      return true;
+    if (Find("_s__"))
+      return true;
+    if (Find("_CatchableType") || Find("_TypeDescriptor"))
+      return true;
+    if (Find("Intermediate\\vctools"))
+      return true;
+    if (Find("$initializer$") || Find("dynamic initializer"))
+      return true;
+    if (Find("`vftable'") || Find("_GLOBAL__sub"))
+      return true;
+    return false;
+  };
+  bool Excluded = CheckExclude();
+  if (Excluded)
+    Element->setIsSystem();
+
+  return Excluded;
+}
+
+Error LVCodeViewReader::collectInlineeInfo(
+    DebugInlineeLinesSubsectionRef &Lines, const llvm::pdb::SymbolGroup *SG) {
+  for (const InlineeSourceLine &Line : Lines) {
+    TypeIndex TIInlinee = Line.Header->Inlinee;
+    uint32_t LineNumber = Line.Header->SourceLineNum;
+    uint32_t FileOffset = Line.Header->FileID;
+    LLVM_DEBUG({
+      DictScope S(W, "InlineeSourceLine");
+      LogicalVisitor.printTypeIndex("Inlinee", TIInlinee, StreamTPI);
+      if (Error Err = printFileNameForOffset("FileID", FileOffset, SG))
+        return Err;
+      W.printNumber("SourceLineNum", LineNumber);
+
+      if (Lines.hasExtraFiles()) {
+        W.printNumber("ExtraFileCount", Line.ExtraFiles.size());
+        ListScope ExtraFiles(W, "ExtraFiles");
+        for (const ulittle32_t &FID : Line.ExtraFiles)
+          if (Error Err = printFileNameForOffset("FileID", FID, SG))
+            return Err;
+      }
+    });
+    Expected<StringRef> NameOrErr = getFileNameForFileOffset(FileOffset, SG);
+    if (!NameOrErr)
+      return NameOrErr.takeError();
+    LogicalVisitor.addInlineeInfo(TIInlinee, LineNumber, *NameOrErr);
+  }
+
+  return Error::success();
+}
+
+Error LVCodeViewReader::traverseInlineeLines(StringRef Subsection) {
+  BinaryStreamReader SR(Subsection, llvm::support::little);
+  DebugInlineeLinesSubsectionRef Lines;
+  if (Error E = Lines.initialize(SR))
+    return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+  return collectInlineeInfo(Lines);
+}
+
+Error LVCodeViewReader::createLines(
+    const FixedStreamArray<LineNumberEntry> &LineNumbers, LVAddress Addendum,
+    uint32_t Segment, uint32_t Begin, uint32_t Size, uint32_t NameIndex,
+    const SymbolGroup *SG) {
+  LLVM_DEBUG({
+    uint32_t End = Begin + Size;
+    W.getOStream() << formatv("{0:x-4}:{1:x-8}-{2:x-8}\n", Segment, Begin, End);
+  });
+
+  for (const LineNumberEntry &Line : LineNumbers) {
+    if (Line.Offset >= Size)
+      return createStringError(object_error::parse_failed, getFileName());
+
+    LineInfo LI(Line.Flags);
+
+    LLVM_DEBUG({
+      W.getOStream() << formatv(
+          "{0} {1:x-8}\n", utostr(LI.getStartLine()),
+          fmt_align(Begin + Line.Offset, AlignStyle::Right, 8, '0'));
+    });
+
+    // The 'processLines()' function will move each created logical line
+    // to its enclosing logical scope, using the debug ranges information
+    // and they will be released when its scope parent is deleted.
+    LVLineDebug *LineDebug = createLineDebug();
+    CULines.push_back(LineDebug);
+    LVAddress Address = linearAddress(Segment, Begin + Line.Offset);
+    LineDebug->setAddress(Address + Addendum);
+
+    if (LI.isAlwaysStepInto())
+      LineDebug->setIsAlwaysStepInto();
+    else if (LI.isNeverStepInto())
+      LineDebug->setIsNeverStepInto();
+    else
+      LineDebug->setLineNumber(LI.getStartLine());
+
+    if (LI.isStatement())
+      LineDebug->setIsNewStatement();
+
+    Expected<StringRef> NameOrErr = getFileNameForFileOffset(NameIndex, SG);
+    if (!NameOrErr)
+      return NameOrErr.takeError();
+    LineDebug->setFilename(*NameOrErr);
+  }
+
+  return Error::success();
+}
+
+Error LVCodeViewReader::initializeFileAndStringTables(
+    BinaryStreamReader &Reader) {
+  while (Reader.bytesRemaining() > 0 &&
+         (!CVFileChecksumTable.valid() || !CVStringTable.valid())) {
+    // The section consists of a number of subsection in the following format:
+    // |SubSectionType|SubSectionSize|Contents...|
+    uint32_t SubType, SubSectionSize;
+
+    if (Error E = Reader.readInteger(SubType))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+    if (Error E = Reader.readInteger(SubSectionSize))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+    StringRef Contents;
+    if (Error E = Reader.readFixedString(Contents, SubSectionSize))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+    BinaryStreamRef ST(Contents, support::little);
+    switch (DebugSubsectionKind(SubType)) {
+    case DebugSubsectionKind::FileChecksums:
+      if (Error E = CVFileChecksumTable.initialize(ST))
+        return createStringError(errorToErrorCode(std::move(E)), getFileName());
+      break;
+    case DebugSubsectionKind::StringTable:
+      if (Error E = CVStringTable.initialize(ST))
+        return createStringError(errorToErrorCode(std::move(E)), getFileName());
+      break;
+    default:
+      break;
+    }
+
+    uint32_t PaddedSize = alignTo(SubSectionSize, 4);
+    if (Error E = Reader.skip(PaddedSize - SubSectionSize))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+  }
+
+  return Error::success();
+}
+
+Error LVCodeViewReader::loadTypeServer(TypeServer2Record &TS) {
+  LLVM_DEBUG({
+    W.printString("Guid", formatv("{0}", TS.getGuid()).str());
+    W.printNumber("Age", TS.getAge());
+    W.printString("Name", TS.getName());
+  });
+
+  SmallString<128> ServerName(TS.getName());
+  BuffOrErr = MemoryBuffer::getFile(ServerName);
+  if (BuffOrErr.getError()) {
+    // The server name does not exist. Try in the same directory as the
+    // input file.
+    ServerName = createAlternativePath(ServerName);
+    BuffOrErr = MemoryBuffer::getFile(ServerName);
+    if (BuffOrErr.getError()) {
+      // For the error message, use the original type server name.
+      return createStringError(errc::bad_file_descriptor,
+                               "File '%s' does not exist.",
+                               TS.getName().str().c_str());
+    }
+  }
+  MemBuffer = std::move(BuffOrErr.get());
+
+  // Check if the buffer corresponds to a PDB file.
+  assert(identify_magic((*MemBuffer).getBuffer()) == file_magic::pdb &&
+         "Invalid PDB file.");
+
+  if (Error Err = loadDataForPDB(PDB_ReaderType::Native, ServerName, Session))
+    return createStringError(errorToErrorCode(std::move(Err)), "%s",
+                             ServerName.c_str());
+
+  PdbSession.reset(static_cast<NativeSession *>(Session.release()));
+  PDBFile &Pdb = PdbSession->getPDBFile();
+
+  // Just because a file with a matching name was found and it was an actual
+  // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
+  // must match the GUID specified in the TypeServer2 record.
+  Expected<InfoStream &> expectedInfo = Pdb.getPDBInfoStream();
+  if (!expectedInfo || expectedInfo->getGuid() != TS.getGuid())
+    return createStringError(errc::invalid_argument, "signature_out_of_date");
+
+  // The reader needs to switch to a type server, to process the types from
+  // the server. We need to keep the original input source, as reading other
+  // sections will require the input associated with the loaded object file.
+  TypeServer = std::make_shared<InputFile>(&Pdb);
+  LogicalVisitor.setInput(TypeServer);
+
+  LazyRandomTypeCollection &Types = types();
+  LazyRandomTypeCollection &Ids = ids();
+  if (Error Err = traverseTypes(Pdb, Types, Ids))
+    return Err;
+
+  return Error::success();
+}
+
+Error LVCodeViewReader::loadPrecompiledObject(PrecompRecord &Precomp,
+                                              CVTypeArray &CVTypesObj) {
+  LLVM_DEBUG({
+    W.printHex("Count", Precomp.getTypesCount());
+    W.printHex("Signature", Precomp.getSignature());
+    W.printString("PrecompFile", Precomp.getPrecompFilePath());
+  });
+
+  SmallString<128> ServerName(Precomp.getPrecompFilePath());
+  BuffOrErr = MemoryBuffer::getFile(ServerName);
+  if (BuffOrErr.getError()) {
+    // The server name does not exist. Try in the directory as the input file.
+    ServerName = createAlternativePath(ServerName);
+    if (BuffOrErr.getError()) {
+      // For the error message, use the original type server name.
+      return createStringError(errc::bad_file_descriptor,
+                               "File '%s' does not exist.",
+                               Precomp.getPrecompFilePath().str().c_str());
+    }
+  }
+  MemBuffer = std::move(BuffOrErr.get());
+
+  Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(*MemBuffer);
+  if (errorToErrorCode(BinOrErr.takeError()))
+    return createStringError(errc::not_supported,
+                             "Binary object format in '%s' is not supported.",
+                             ServerName.c_str());
+
+  Binary &BinaryObj = *BinOrErr.get();
+  if (!BinaryObj.isCOFF())
+    return createStringError(errc::not_supported, "'%s' is not a COFF object.",
+                             ServerName.c_str());
+
+  Builder = std::make_unique<AppendingTypeTableBuilder>(BuilderAllocator);
+
+  // The MSVC precompiled header object file, should contain just a single
+  // ".debug$P" section.
+  COFFObjectFile &Obj = *cast<COFFObjectFile>(&BinaryObj);
+  for (const SectionRef &Section : Obj.sections()) {
+    Expected<StringRef> SectionNameOrErr = Section.getName();
+    if (!SectionNameOrErr)
+      return SectionNameOrErr.takeError();
+    if (*SectionNameOrErr == ".debug$P") {
+      Expected<StringRef> DataOrErr = Section.getContents();
+      if (!DataOrErr)
+        return DataOrErr.takeError();
+      uint32_t Magic;
+      if (Error Err = consume(*DataOrErr, Magic))
+        return Err;
+      if (Magic != COFF::DEBUG_SECTION_MAGIC)
+        return errorCodeToError(object_error::parse_failed);
+
+      ReaderPrecomp =
+          std::make_unique<BinaryStreamReader>(*DataOrErr, support::little);
+      cantFail(
+          ReaderPrecomp->readArray(CVTypesPrecomp, ReaderPrecomp->getLength()));
+
+      // Append all the type records up to the LF_ENDPRECOMP marker and
+      // check if the signatures match.
+      for (const CVType &Type : CVTypesPrecomp) {
+        ArrayRef<uint8_t> TypeData = Type.data();
+        if (Type.kind() == LF_ENDPRECOMP) {
+          EndPrecompRecord EndPrecomp = cantFail(
+              TypeDeserializer::deserializeAs<EndPrecompRecord>(TypeData));
+          if (Precomp.getSignature() != EndPrecomp.getSignature())
+            return createStringError(errc::invalid_argument, "no matching pch");
+          break;
+        }
+        Builder->insertRecordBytes(TypeData);
+      }
+      // Done processing .debug$P, break out of section loop.
+      break;
+    }
+  }
+
+  // Append all the type records, skipping the first record which is the
+  // reference to the precompiled header object information.
+  for (const CVType &Type : CVTypesObj) {
+    ArrayRef<uint8_t> TypeData = Type.data();
+    if (Type.kind() != LF_PRECOMP)
+      Builder->insertRecordBytes(TypeData);
+  }
+
+  // Set up a type stream that refers to the added type records.
+  Builder->ForEachRecord(
+      [&](TypeIndex TI, const CVType &Type) { TypeArray.push_back(Type); });
+
+  ItemStream =
+      std::make_unique<BinaryItemStream<CVType>>(llvm::support::little);
+  ItemStream->setItems(TypeArray);
+  TypeStream.setUnderlyingStream(*ItemStream);
+
+  PrecompHeader =
+      std::make_shared<LazyRandomTypeCollection>(TypeStream, TypeArray.size());
+
+  // Change the original input source to use the collected type records.
+  LogicalVisitor.setInput(PrecompHeader);
+
+  LazyRandomTypeCollection &Types = types();
+  LazyRandomTypeCollection &Ids = ids();
+  LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI,
+                    LogicalVisitor.getShared());
+  return visitTypeStream(Types, TDV);
+}
+
+Error LVCodeViewReader::traverseTypeSection(StringRef SectionName,
+                                            const SectionRef &Section) {
+  LLVM_DEBUG({
+    ListScope D(W, "CodeViewTypes");
+    W.printNumber("Section", SectionName, getObj().getSectionID(Section));
+  });
+
+  Expected<StringRef> DataOrErr = Section.getContents();
+  if (!DataOrErr)
+    return DataOrErr.takeError();
+  uint32_t Magic;
+  if (Error Err = consume(*DataOrErr, Magic))
+    return Err;
+  if (Magic != COFF::DEBUG_SECTION_MAGIC)
+    return errorCodeToError(object_error::parse_failed);
+
+  // Get the first type record. It will indicate if this object uses a type
+  // server (/Zi) or a PCH file (/Yu).
+  CVTypeArray CVTypes;
+  BinaryStreamReader Reader(*DataOrErr, support::little);
+  cantFail(Reader.readArray(CVTypes, Reader.getLength()));
+  CVTypeArray::Iterator FirstType = CVTypes.begin();
+
+  // The object was compiled with /Zi. It uses types from a type server PDB.
+  if (FirstType->kind() == LF_TYPESERVER2) {
+    TypeServer2Record TS = cantFail(
+        TypeDeserializer::deserializeAs<TypeServer2Record>(FirstType->data()));
+    return loadTypeServer(TS);
+  }
+
+  // The object was compiled with /Yc or /Yu. It uses types from another
+  // object file with a matching signature.
+  if (FirstType->kind() == LF_PRECOMP) {
+    PrecompRecord Precomp = cantFail(
+        TypeDeserializer::deserializeAs<PrecompRecord>(FirstType->data()));
+    return loadPrecompiledObject(Precomp, CVTypes);
+  }
+
+  LazyRandomTypeCollection &Types = types();
+  LazyRandomTypeCollection &Ids = ids();
+  Types.reset(*DataOrErr, 100);
+  LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI,
+                    LogicalVisitor.getShared());
+  return visitTypeStream(Types, TDV);
+}
+
+Error LVCodeViewReader::traverseTypes(PDBFile &Pdb,
+                                      LazyRandomTypeCollection &Types,
+                                      LazyRandomTypeCollection &Ids) {
+  // Traverse types (TPI and IPI).
+  auto VisitTypes = [&](LazyRandomTypeCollection &Types,
+                        LazyRandomTypeCollection &Ids,
+                        SpecialStream StreamIdx) -> Error {
+    LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamIdx,
+                      LogicalVisitor.getShared());
+    return visitTypeStream(Types, TDV);
+  };
+
+  Expected<TpiStream &> StreamTpiOrErr = Pdb.getPDBTpiStream();
+  if (!StreamTpiOrErr)
+    return StreamTpiOrErr.takeError();
+  TpiStream &StreamTpi = *StreamTpiOrErr;
+  StreamTpi.buildHashMap();
+  LLVM_DEBUG({
+    W.getOStream() << formatv("Showing {0:N} TPI records\n",
+                              StreamTpi.getNumTypeRecords());
+  });
+  if (Error Err = VisitTypes(Types, Ids, StreamTPI))
+    return Err;
+
+  Expected<TpiStream &> StreamIpiOrErr = Pdb.getPDBIpiStream();
+  if (!StreamIpiOrErr)
+    return StreamIpiOrErr.takeError();
+  TpiStream &StreamIpi = *StreamIpiOrErr;
+  StreamIpi.buildHashMap();
+  LLVM_DEBUG({
+    W.getOStream() << formatv("Showing {0:N} IPI records\n",
+                              StreamIpi.getNumTypeRecords());
+  });
+  return VisitTypes(Ids, Ids, StreamIPI);
+}
+
+Error LVCodeViewReader::traverseSymbolsSubsection(StringRef Subsection,
+                                                  const SectionRef &Section,
+                                                  StringRef SectionContents) {
+  ArrayRef<uint8_t> BinaryData(Subsection.bytes_begin(),
+                               Subsection.bytes_end());
+  LVSymbolVisitorDelegate VisitorDelegate(this, Section, &getObj(),
+                                          SectionContents);
+  CVSymbolArray Symbols;
+  BinaryStreamReader Reader(BinaryData, llvm::support::little);
+  if (Error E = Reader.readArray(Symbols, Reader.getLength()))
+    return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+  LazyRandomTypeCollection &Types = types();
+  LazyRandomTypeCollection &Ids = ids();
+  SymbolVisitorCallbackPipeline Pipeline;
+  SymbolDeserializer Deserializer(&VisitorDelegate,
+                                  CodeViewContainer::ObjectFile);
+  // As we are processing a COFF format, use TPI as IPI, so the generic code
+  // to process the CodeView format does not contain any additional checks.
+  LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids,
+                            &VisitorDelegate, LogicalVisitor.getShared());
+
+  Pipeline.addCallbackToPipeline(Deserializer);
+  Pipeline.addCallbackToPipeline(Traverser);
+  CVSymbolVisitor Visitor(Pipeline);
+  return Visitor.visitSymbolStream(Symbols);
+}
+
+Error LVCodeViewReader::traverseSymbolSection(StringRef SectionName,
+                                              const SectionRef &Section) {
+  LLVM_DEBUG({
+    ListScope D(W, "CodeViewDebugInfo");
+    W.printNumber("Section", SectionName, getObj().getSectionID(Section));
+  });
+
+  Expected<StringRef> SectionOrErr = Section.getContents();
+  if (!SectionOrErr)
+    return SectionOrErr.takeError();
+  StringRef SectionContents = *SectionOrErr;
+  StringRef Data = SectionContents;
+
+  SmallVector<StringRef, 10> SymbolNames;
+  StringMap<StringRef> FunctionLineTables;
+
+  uint32_t Magic;
+  if (Error E = consume(Data, Magic))
+    return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+  if (Magic != COFF::DEBUG_SECTION_MAGIC)
+    return createStringError(object_error::parse_failed, getFileName());
+
+  BinaryStreamReader FSReader(Data, support::little);
+  if (Error Err = initializeFileAndStringTables(FSReader))
+    return Err;
+
+  while (!Data.empty()) {
+    // The section consists of a number of subsection in the following format:
+    // |SubSectionType|SubSectionSize|Contents...|
+    uint32_t SubType, SubSectionSize;
+    if (Error E = consume(Data, SubType))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+    if (Error E = consume(Data, SubSectionSize))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+    // Process the subsection as normal even if the ignore bit is set.
+    SubType &= ~SubsectionIgnoreFlag;
+
+    // Get the contents of the subsection.
+    if (SubSectionSize > Data.size())
+      return createStringError(object_error::parse_failed, getFileName());
+    StringRef Contents = Data.substr(0, SubSectionSize);
+
+    // Add SubSectionSize to the current offset and align that offset
+    // to find the next subsection.
+    size_t SectionOffset = Data.data() - SectionContents.data();
+    size_t NextOffset = SectionOffset + SubSectionSize;
+    NextOffset = alignTo(NextOffset, 4);
+    if (NextOffset > SectionContents.size())
+      return createStringError(object_error::parse_failed, getFileName());
+    Data = SectionContents.drop_front(NextOffset);
+
+    switch (DebugSubsectionKind(SubType)) {
+    case DebugSubsectionKind::Symbols:
+      if (Error Err =
+              traverseSymbolsSubsection(Contents, Section, SectionContents))
+        return Err;
+      break;
+
+    case DebugSubsectionKind::InlineeLines:
+      if (Error Err = traverseInlineeLines(Contents))
+        return Err;
+      break;
+
+    case DebugSubsectionKind::Lines:
+      // Holds a PC to file:line table. Some data to parse this subsection
+      // is stored in the other subsections, so just check sanity and store
+      // the pointers for deferred processing.
+
+      // Collect function and ranges only if we need to print logical lines.
+      if (options().getGeneralCollectRanges()) {
+
+        if (SubSectionSize < 12) {
+          // There should be at least three words to store two function
+          // relocations and size of the code.
+          return createStringError(object_error::parse_failed, getFileName());
+        }
+
+        StringRef SymbolName;
+        if (Error Err = resolveSymbolName(getObj().getCOFFSection(Section),
+                                          SectionOffset, SymbolName))
+          return createStringError(errorToErrorCode(std::move(Err)),
+                                   getFileName());
+
+        LLVM_DEBUG({ W.printString("Symbol Name", SymbolName); });
+        if (FunctionLineTables.count(SymbolName) != 0) {
+          // Saw debug info for this function already?
+          return createStringError(object_error::parse_failed, getFileName());
+        }
+
+        FunctionLineTables[SymbolName] = Contents;
+        SymbolNames.push_back(SymbolName);
+      }
+      break;
+
+    // Do nothing for unrecognized subsections.
+    default:
+      break;
+    }
+    W.flush();
+  }
+
+  // Traverse the line tables now that we've read all the subsections and
+  // know all the required information.
+  for (StringRef SymbolName : SymbolNames) {
+    LLVM_DEBUG({
+      ListScope S(W, "FunctionLineTable");
+      W.printString("Symbol Name", SymbolName);
+    });
+
+    BinaryStreamReader Reader(FunctionLineTables[SymbolName], support::little);
+
+    DebugLinesSubsectionRef Lines;
+    if (Error E = Lines.initialize(Reader))
+      return createStringError(errorToErrorCode(std::move(E)), getFileName());
+
+    // Find the associated symbol table information.
+    LVSymbolTableEntry SymbolTableEntry = getSymbolTableEntry(SymbolName);
+    LVScope *Function = SymbolTableEntry.Scope;
+    if (!Function)
+      continue;
+
+    LVAddress Addendum = SymbolTableEntry.Address;
+    LVSectionIndex SectionIndex = SymbolTableEntry.SectionIndex;
+
+    // The given scope represents the function that contains the line numbers.
+    // Collect all generated debug lines associated with the function.
+    CULines.clear();
+
+    // For the given scope, collect all scopes ranges.
+    LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);
+    ScopesWithRanges->clear();
+    Function->getRanges(*ScopesWithRanges);
+    ScopesWithRanges->sort();
+
+    uint16_t Segment = Lines.header()->RelocSegment;
+    uint32_t Begin = Lines.header()->RelocOffset;
+    uint32_t Size = Lines.header()->CodeSize;
+    for (const LineColumnEntry &Block : Lines)
+      if (Error Err = createLines(Block.LineNumbers, Addendum, Segment, Begin,
+                                  Size, Block.NameIndex))
+        return Err;
+
+    // Include lines from any inlined functions within the current function.
+    includeInlineeLines(SectionIndex, Function);
+
+    if (Error Err = createInstructions(Function, SectionIndex))
+      return Err;
+
+    processLines(&CULines, SectionIndex, Function);
+  }
+
+  return Error::success();
+}
+
+void LVCodeViewReader::sortScopes() { Root->sort(); }
+
+void LVCodeViewReader::print(raw_ostream &OS) const {
+  LLVM_DEBUG(dbgs() << "CreateReaders\n");
+}
+
+void LVCodeViewReader::mapRangeAddress(const ObjectFile &Obj,
+                                       const SectionRef &Section,
+                                       bool IsComdat) {
+  if (!Obj.isCOFF())
+    return;
+
+  const COFFObjectFile *Object = cast<COFFObjectFile>(&Obj);
+
+  for (const SymbolRef &Sym : Object->symbols()) {
+    if (!Section.containsSymbol(Sym))
+      continue;
+
+    COFFSymbolRef Symbol = Object->getCOFFSymbol(Sym);
+    if (Symbol.getComplexType() != llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION)
+      continue;
+
+    StringRef SymbolName;
+    Expected<StringRef> SymNameOrErr = Object->getSymbolName(Symbol);
+    if (!SymNameOrErr) {
+      W.startLine() << "Invalid symbol name: " << Symbol.getSectionNumber()
+                    << "\n";
+      consumeError(SymNameOrErr.takeError());
+      continue;
+    }
+    SymbolName = *SymNameOrErr;
+
+    LLVM_DEBUG({
+      Expected<const coff_section *> SectionOrErr =
+          Object->getSection(Symbol.getSectionNumber());
+      if (!SectionOrErr) {
+        W.startLine() << "Invalid section number: " << Symbol.getSectionNumber()
+                      << "\n";
+        consumeError(SectionOrErr.takeError());
+        return;
+      }
+      W.printNumber("Section #", Symbol.getSectionNumber());
+      W.printString("Name", SymbolName);
+      W.printHex("Value", Symbol.getValue());
+    });
+
+    // Record the symbol name (linkage) and its loading address.
+    addToSymbolTable(SymbolName, Symbol.getValue(), Symbol.getSectionNumber(),
+                     IsComdat);
+  }
+}
+
+Error LVCodeViewReader::createScopes(COFFObjectFile &Obj) {
+  if (Error Err = loadTargetInfo(Obj))
+    return Err;
+
+  // Initialization required when processing a COFF file:
+  // Cache the symbols relocations.
+  // Create a mapping for virtual addresses.
+  // Get the functions entry points.
+  cacheRelocations();
+  mapVirtualAddress(Obj);
+
+  for (const SectionRef &Section : Obj.sections()) {
+    Expected<StringRef> SectionNameOrErr = Section.getName();
+    if (!SectionNameOrErr)
+      return SectionNameOrErr.takeError();
+    // .debug$T is a standard CodeView type section, while .debug$P is the
+    // same format but used for MSVC precompiled header object files.
+    if (*SectionNameOrErr == ".debug$T" || *SectionNameOrErr == ".debug$P")
+      if (Error Err = traverseTypeSection(*SectionNameOrErr, Section))
+        return Err;
+  }
+
+  // Process collected namespaces.
+  LogicalVisitor.processNamespaces();
+
+  for (const SectionRef &Section : Obj.sections()) {
+    Expected<StringRef> SectionNameOrErr = Section.getName();
+    if (!SectionNameOrErr)
+      return SectionNameOrErr.takeError();
+    if (*SectionNameOrErr == ".debug$S")
+      if (Error Err = traverseSymbolSection(*SectionNameOrErr, Section))
+        return Err;
+  }
+
+  // Check if we have to close the Compile Unit scope.
+  LogicalVisitor.closeScope();
+
+  // Traverse the strings recorded and transform them into filenames.
+  LogicalVisitor.processFiles();
+
+  // Process collected element lines.
+  LogicalVisitor.processLines();
+
+  // Translate composite names into a single component.
+  Root->transformScopedName();
+  return Error::success();
+}
+
+Error LVCodeViewReader::createScopes(PDBFile &Pdb) {
+  if (Error Err = loadTargetInfo(Pdb))
+    return Err;
+
+  if (!Pdb.hasPDBTpiStream() || !Pdb.hasPDBDbiStream())
+    return Error::success();
+
+  // Open the executable associated with the PDB file and get the section
+  // addresses used to calculate linear addresses for CodeView Symbols.
+  if (!ExePath.empty()) {
+    ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+        MemoryBuffer::getFileOrSTDIN(ExePath);
+    if (BuffOrErr.getError()) {
+      return createStringError(errc::bad_file_descriptor,
+                               "File '%s' does not exist.", ExePath.c_str());
+    }
+    BinaryBuffer = std::move(BuffOrErr.get());
+
+    // Check if the buffer corresponds to a PECOFF executable.
+    assert(identify_magic(BinaryBuffer->getBuffer()) ==
+               file_magic::pecoff_executable &&
+           "Invalid PECOFF executable file.");
+
+    Expected<std::unique_ptr<Binary>> BinOrErr =
+        createBinary(BinaryBuffer->getMemBufferRef());
+    if (errorToErrorCode(BinOrErr.takeError())) {
+      return createStringError(errc::not_supported,
+                               "Binary object format in '%s' is not supported.",
+                               ExePath.c_str());
+    }
+    BinaryExecutable = std::move(*BinOrErr);
+    if (COFFObjectFile *COFFObject =
+            dyn_cast<COFFObjectFile>(BinaryExecutable.get()))
+      mapVirtualAddress(*COFFObject);
+  }
+
+  // In order to generate a full logical view, we have to traverse both
+  // streams TPI and IPI if they are present. The following table gives
+  // the stream where a specified type is located. If the IPI stream is
+  // not present, all the types are located in the TPI stream.
+  //
+  // TPI Stream:
+  //   LF_POINTER   LF_MODIFIER     LF_PROCEDURE    LF_MFUNCTION
+  //   LF_LABEL     LF_ARGLIST      LF_FIELDLIST    LF_ARRAY
+  //   LF_CLASS     LF_STRUCTURE    LF_INTERFACE    LF_UNION
+  //   LF_ENUM      LF_TYPESERVER2  LF_VFTABLE      LF_VTSHAPE
+  //   LF_BITFIELD  LF_METHODLIST   LF_PRECOMP      LF_ENDPRECOMP
+  //
+  // IPI stream:
+  //   LF_FUNC_ID           LF_MFUNC_ID   LF_BUILDINFO
+  //   LF_SUBSTR_LIST       LF_STRING_ID  LF_UDT_SRC_LINE
+  //   LF_UDT_MOD_SRC_LINE
+
+  LazyRandomTypeCollection &Types = types();
+  LazyRandomTypeCollection &Ids = ids();
+  if (Error Err = traverseTypes(Pdb, Types, Ids))
+    return Err;
+
+  // Process collected namespaces.
+  LogicalVisitor.processNamespaces();
+
+  LLVM_DEBUG({ W.getOStream() << "Traversing inlined lines\n"; });
+
+  auto VisitInlineeLines = [&](int32_t Modi, const SymbolGroup &SG,
+                               DebugInlineeLinesSubsectionRef &Lines) -> Error {
+    return collectInlineeInfo(Lines, &SG);
+  };
+
+  FilterOptions Filters = {};
+  LinePrinter Printer(/*Indent=*/2, false, nulls(), Filters);
+  const PrintScope HeaderScope(Printer, /*IndentLevel=*/2);
+  if (Error Err = iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
+          Input, HeaderScope, VisitInlineeLines))
+    return Err;
+
+  // Traverse global symbols.
+  LLVM_DEBUG({ W.getOStream() << "Traversing global symbols\n"; });
+  if (Pdb.hasPDBGlobalsStream()) {
+    Expected<GlobalsStream &> GlobalsOrErr = Pdb.getPDBGlobalsStream();
+    if (!GlobalsOrErr)
+      return GlobalsOrErr.takeError();
+    GlobalsStream &Globals = *GlobalsOrErr;
+    const GSIHashTable &Table = Globals.getGlobalsTable();
+    Expected<SymbolStream &> ExpectedSyms = Pdb.getPDBSymbolStream();
+    if (ExpectedSyms) {
+
+      SymbolVisitorCallbackPipeline Pipeline;
+      SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+      LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr,
+                                LogicalVisitor.getShared());
+
+      // As the global symbols do not have an associated Compile Unit, create
+      // one, as the container for all global symbols.
+      RecordPrefix Prefix(SymbolKind::S_COMPILE3);
+      CVSymbol Symbol(&Prefix, sizeof(Prefix));
+      uint32_t Offset = 0;
+      if (Error Err = Traverser.visitSymbolBegin(Symbol, Offset))
+        consumeError(std::move(Err));
+      else {
+        // The CodeView compile unit containing the global symbols does not
+        // have a name; generate one using its parent name (object filename)
+        // follow by the '_global' string.
+        std::string Name(CompileUnit->getParentScope()->getName());
+        CompileUnit->setName(Name.append("_global"));
+
+        Pipeline.addCallbackToPipeline(Deserializer);
+        Pipeline.addCallbackToPipeline(Traverser);
+        CVSymbolVisitor Visitor(Pipeline);
+
+        BinaryStreamRef SymStream =
+            ExpectedSyms->getSymbolArray().getUnderlyingStream();
+        for (uint32_t PubSymOff : Table) {
+          Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
+          if (Sym) {
+            if (Error Err = Visitor.visitSymbolRecord(*Sym, PubSymOff))
+              return createStringError(errorToErrorCode(std::move(Err)),
+                                       getFileName());
+          } else {
+            consumeError(Sym.takeError());
+          }
+        }
+      }
+
+      LogicalVisitor.closeScope();
+    } else {
+      consumeError(ExpectedSyms.takeError());
+    }
+  }
+
+  // Traverse symbols (DBI).
+  LLVM_DEBUG({ W.getOStream() << "Traversing symbol groups\n"; });
+
+  auto VisitSymbolGroup = [&](uint32_t Modi, const SymbolGroup &SG) -> Error {
+    Expected<ModuleDebugStreamRef> ExpectedModS =
+        getModuleDebugStream(Pdb, Modi);
+    if (ExpectedModS) {
+      ModuleDebugStreamRef &ModS = *ExpectedModS;
+
+      LLVM_DEBUG({
+        W.getOStream() << formatv("Traversing Group: Mod {0:4}\n", Modi);
+      });
+
+      SymbolVisitorCallbackPipeline Pipeline;
+      SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+      LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr,
+                                LogicalVisitor.getShared());
+
+      Pipeline.addCallbackToPipeline(Deserializer);
+      Pipeline.addCallbackToPipeline(Traverser);
+      CVSymbolVisitor Visitor(Pipeline);
+      BinarySubstreamRef SS = ModS.getSymbolsSubstream();
+      if (Error Err =
+              Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset))
+        return createStringError(errorToErrorCode(std::move(Err)),
+                                 getFileName());
+    } else {
+      // If the module stream does not exist, it is not an error condition.
+      consumeError(ExpectedModS.takeError());
+    }
+
+    return Error::success();
+  };
+
+  if (Error Err = iterateSymbolGroups(Input, HeaderScope, VisitSymbolGroup))
+    return Err;
+
+  // At this stage, the logical view contains all scopes, symbols and types.
+  // For PDBs we can use the module id, to access its specific compile unit.
+  // The line record addresses has been already resolved, so we can apply the
+  // flow as when processing DWARF.
+
+  LLVM_DEBUG({ W.getOStream() << "Traversing lines\n"; });
+
+  // Record all line records for a Compile Unit.
+  CULines.clear();
+
+  auto VisitDebugLines = [this](int32_t Modi, const SymbolGroup &SG,
+                                DebugLinesSubsectionRef &Lines) -> Error {
+    if (!options().getPrintLines())
+      return Error::success();
+
+    uint16_t Segment = Lines.header()->RelocSegment;
+    uint32_t Begin = Lines.header()->RelocOffset;
+    uint32_t Size = Lines.header()->CodeSize;
+
+    LLVM_DEBUG({ W.getOStream() << formatv("Modi = {0}\n", Modi); });
+
+    // We have line information for a new module; finish processing the
+    // collected information for the current module. Once it is done, start
+    // recording the line information for the new module.
+    if (CurrentModule != Modi) {
+      if (Error Err = processModule())
+        return Err;
+      CULines.clear();
+      CurrentModule = Modi;
+    }
+
+    for (const LineColumnEntry &Block : Lines)
+      if (Error Err = createLines(Block.LineNumbers, /*Addendum=*/0, Segment,
+                                  Begin, Size, Block.NameIndex, &SG))
+        return Err;
+
+    return Error::success();
+  };
+
+  if (Error Err = iterateModuleSubsections<DebugLinesSubsectionRef>(
+          Input, HeaderScope, VisitDebugLines))
+    return Err;
+
+  // Check if we have to close the Compile Unit scope.
+  LogicalVisitor.closeScope();
+
+  // Process collected element lines.
+  LogicalVisitor.processLines();
+
+  // Translate composite names into a single component.
+  Root->transformScopedName();
+  return Error::success();
+}
+
+Error LVCodeViewReader::processModule() {
+  if (LVScope *Scope = getScopeForModule(CurrentModule)) {
+    CompileUnit = static_cast<LVScopeCompileUnit *>(Scope);
+
+    LLVM_DEBUG({ dbgs() << "Processing Scope: " << Scope->getName() << "\n"; });
+
+    // For the given compile unit, collect all scopes ranges.
+    // For a complete ranges and lines mapping, the logical view support
+    // needs for the compile unit to have a low and high pc values. We
+    // can traverse the 'Modules' section and get the information for the
+    // specific module. Another option, is from all the ranges collected
+    // to take the first and last values.
+    LVSectionIndex SectionIndex = DotTextSectionIndex;
+    LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);
+    ScopesWithRanges->clear();
+    CompileUnit->getRanges(*ScopesWithRanges);
+    if (!ScopesWithRanges->empty())
+      CompileUnit->addObject(ScopesWithRanges->getLower(),
+                             ScopesWithRanges->getUpper());
+    ScopesWithRanges->sort();
+
+    if (Error Err = createInstructions())
+      return Err;
+
+    // Include lines from any inlined functions within the current function.
+    includeInlineeLines(SectionIndex, Scope);
+
+    processLines(&CULines, SectionIndex, nullptr);
+  }
+
+  return Error::success();
+}
+
+// In order to create the scopes, the CodeView Reader will:
+// = Traverse the TPI/IPI stream (Type visitor):
+// Collect forward references, scoped names, type indexes that will represent
+// a logical element, strings, line records, linkage names.
+// = Traverse the symbols section (Symbol visitor):
+// Create the scopes tree and creates the required logical elements, by
+// using the collected indexes from the type visitor.
+Error LVCodeViewReader::createScopes() {
+  LLVM_DEBUG({
+    W.startLine() << "\n";
+    W.printString("File", getFileName().str());
+    W.printString("Exe", ExePath);
+    W.printString("Format", FileFormatName);
+  });
+
+  if (Error Err = LVReader::createScopes())
+    return Err;
+
+  LogicalVisitor.setRoot(Root);
+
+  if (isObj()) {
+    if (Error Err = createScopes(getObj()))
+      return Err;
+  } else {
+    if (Error Err = createScopes(getPdb()))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+Error LVCodeViewReader::loadTargetInfo(const ObjectFile &Obj) {
+  // Detect the architecture from the object file. We usually don't need OS
+  // info to lookup a target and create register info.
+  Triple TT;
+  TT.setArch(Triple::ArchType(Obj.getArch()));
+  TT.setVendor(Triple::UnknownVendor);
+  TT.setOS(Triple::UnknownOS);
+
+  // Features to be passed to target/subtarget
+  Expected<SubtargetFeatures> Features = Obj.getFeatures();
+  SubtargetFeatures FeaturesValue;
+  if (!Features) {
+    consumeError(Features.takeError());
+    FeaturesValue = SubtargetFeatures();
+  }
+  FeaturesValue = *Features;
+  return loadGenericTargetInfo(TT.str(), FeaturesValue.getString());
+}
+
+Error LVCodeViewReader::loadTargetInfo(const PDBFile &Pdb) {
+  Triple TT;
+  TT.setArch(Triple::ArchType::x86_64);
+  TT.setVendor(Triple::UnknownVendor);
+  TT.setOS(Triple::Win32);
+
+  StringRef TheFeature = "";
+
+  return loadGenericTargetInfo(TT.str(), TheFeature);
+}
+
+std::string LVCodeViewReader::getRegisterName(LVSmall Opcode,
+                                              uint64_t Operands[2]) {
+  // Get Compilation Unit CPU Type.
+  CPUType CPU = getCompileUnitCPUType();
+  // For CodeView the register always is in Operands[0];
+  RegisterId Register = (RegisterId(Operands[0]));
+  return formatRegisterId(Register, CPU);
+}

diff  --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
new file mode 100644
index 0000000000000..2139124873aed
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
@@ -0,0 +1,3525 @@
+//===-- LVCodeViewVisitor.cpp ---------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVCodeViewVisitor class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/DebugInfo/CodeView/EnumTables.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
+#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InputFile.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::object;
+using namespace llvm::pdb;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "CodeViewUtilities"
+
+namespace llvm {
+namespace logicalview {
+
+static TypeIndex getTrueType(TypeIndex &TI) {
+  // Dealing with a MSVC generated PDB, we encountered a type index with the
+  // value of: 0x0280xxxx where xxxx=0000.
+  //
+  // There is some documentation about type indices:
+  // https://llvm.org/docs/PDB/TpiStream.html
+  //
+  // A type index is a 32-bit integer that uniquely identifies a type inside
+  // of an object file’s .debug$T section or a PDB file’s TPI or IPI stream.
+  // The value of the type index for the first type record from the TPI stream
+  // is given by the TypeIndexBegin member of the TPI Stream Header although
+  // in practice this value is always equal to 0x1000 (4096).
+  //
+  // Any type index with a high bit set is considered to come from the IPI
+  // stream, although this appears to be more of a hack, and LLVM does not
+  // generate type indices of this nature. They can, however, be observed in
+  // Microsoft PDBs occasionally, so one should be prepared to handle them.
+  // Note that having the high bit set is not a necessary condition to
+  // determine whether a type index comes from the IPI stream, it is only
+  // sufficient.
+  LLVM_DEBUG(
+      { dbgs() << "Index before: " << HexNumber(TI.getIndex()) << "\n"; });
+  TI.setIndex(TI.getIndex() & 0x0000ffff);
+  LLVM_DEBUG(
+      { dbgs() << "Index after: " << HexNumber(TI.getIndex()) << "\n"; });
+  return TI;
+}
+
+static const EnumEntry<TypeLeafKind> LeafTypeNames[] = {
+#define CV_TYPE(enum, val) {#enum, enum},
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+};
+
+// Return the type name pointed by the type index. It uses the kind to query
+// the associated name for the record type.
+static StringRef getRecordName(LazyRandomTypeCollection &Types, TypeIndex TI) {
+  if (TI.isSimple())
+    return {};
+
+  StringRef RecordName;
+  CVType CVReference = Types.getType(TI);
+  auto GetName = [&](auto Record) {
+    if (Error Err = TypeDeserializer::deserializeAs(
+            const_cast<CVType &>(CVReference), Record))
+      consumeError(std::move(Err));
+    else
+      RecordName = Record.getName();
+  };
+
+  TypeRecordKind RK = static_cast<TypeRecordKind>(CVReference.kind());
+  if (RK == TypeRecordKind::Class || RK == TypeRecordKind::Struct)
+    GetName(ClassRecord(RK));
+  else if (RK == TypeRecordKind::Union)
+    GetName(UnionRecord(RK));
+  else if (RK == TypeRecordKind::Enum)
+    GetName(EnumRecord(RK));
+
+  return RecordName;
+}
+
+} // namespace logicalview
+} // namespace llvm
+
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "CodeViewDataVisitor"
+
+namespace llvm {
+namespace logicalview {
+
+// Keeps the type indexes with line information.
+using LVLineRecords = std::vector<TypeIndex>;
+
+namespace {
+
+class LVTypeRecords {
+  LVShared *Shared = nullptr;
+
+  // Logical elements associated to their CodeView Type Index.
+  using RecordEntry = std::pair<TypeLeafKind, LVElement *>;
+  using RecordTable = std::map<TypeIndex, RecordEntry>;
+  RecordTable RecordFromTypes;
+  RecordTable RecordFromIds;
+
+  using NameTable = std::map<StringRef, TypeIndex>;
+  NameTable NameFromTypes;
+  NameTable NameFromIds;
+
+public:
+  LVTypeRecords(LVShared *Shared) : Shared(Shared) {}
+
+  void add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind,
+           LVElement *Element = nullptr);
+  void add(uint32_t StreamIdx, TypeIndex TI, StringRef Name);
+  LVElement *find(uint32_t StreamIdx, TypeIndex TI, bool Create = true);
+  TypeIndex find(uint32_t StreamIdx, StringRef Name);
+};
+
+class LVForwardReferences {
+  // Forward reference and its definitions (Name as key).
+  using ForwardEntry = std::pair<TypeIndex, TypeIndex>;
+  using ForwardTypeNames = std::map<StringRef, ForwardEntry>;
+  ForwardTypeNames ForwardTypesNames;
+
+  // Forward reference and its definition (TypeIndex as key).
+  using ForwardType = std::map<TypeIndex, TypeIndex>;
+  ForwardType ForwardTypes;
+
+  // Forward types and its references.
+  void add(TypeIndex TIForward, TypeIndex TIReference) {
+    ForwardTypes.emplace(TIForward, TIReference);
+  }
+
+  void add(StringRef Name, TypeIndex TIForward) {
+    if (ForwardTypesNames.find(Name) == ForwardTypesNames.end()) {
+      ForwardTypesNames.emplace(
+          std::piecewise_construct, std::forward_as_tuple(Name),
+          std::forward_as_tuple(TIForward, TypeIndex::None()));
+    } else {
+      // Update a recorded definition with its reference.
+      ForwardTypesNames[Name].first = TIForward;
+      add(TIForward, ForwardTypesNames[Name].second);
+    }
+  }
+
+  // Update a previously recorded forward reference with its definition.
+  void update(StringRef Name, TypeIndex TIReference) {
+    if (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) {
+      // Update the recorded forward reference with its definition.
+      ForwardTypesNames[Name].second = TIReference;
+      add(ForwardTypesNames[Name].first, TIReference);
+    } else {
+      // We have not seen the forward reference. Insert the definition.
+      ForwardTypesNames.emplace(
+          std::piecewise_construct, std::forward_as_tuple(Name),
+          std::forward_as_tuple(TypeIndex::None(), TIReference));
+    }
+  }
+
+public:
+  LVForwardReferences() = default;
+
+  void record(bool IsForwardRef, StringRef Name, TypeIndex TI) {
+    // We are expecting for the forward references to be first. But that
+    // is not always the case. A name must be recorded regardless of the
+    // order in which the forward reference appears.
+    (IsForwardRef) ? add(Name, TI) : update(Name, TI);
+  }
+
+  TypeIndex find(TypeIndex TIForward) {
+    return (ForwardTypes.find(TIForward) != ForwardTypes.end())
+               ? ForwardTypes[TIForward]
+               : TypeIndex::None();
+  }
+
+  TypeIndex find(StringRef Name) {
+    return (ForwardTypesNames.find(Name) != ForwardTypesNames.end())
+               ? ForwardTypesNames[Name].second
+               : TypeIndex::None();
+  }
+
+  // If the given TI corresponds to a reference, return the reference.
+  // Otherwise return the given TI.
+  TypeIndex remap(TypeIndex TI) {
+    TypeIndex Forward = find(TI);
+    return Forward.isNoneType() ? TI : Forward;
+  }
+};
+
+// Namespace deduction.
+class LVNamespaceDeduction {
+  LVShared *Shared = nullptr;
+
+  using Names = std::map<StringRef, LVScope *>;
+  Names NamespaceNames;
+
+  using LookupSet = std::set<StringRef>;
+  LookupSet DeducedScopes;
+  LookupSet UnresolvedScopes;
+  LookupSet IdentifiedNamespaces;
+
+  void add(StringRef Name, LVScope *Namespace) {
+    if (NamespaceNames.find(Name) == NamespaceNames.end())
+      NamespaceNames.emplace(Name, Namespace);
+  }
+
+public:
+  LVNamespaceDeduction(LVShared *Shared) : Shared(Shared) {}
+
+  void init();
+  void add(StringRef String);
+  LVScope *get(LVStringRefs Components);
+  LVScope *get(StringRef Name, bool CheckScope = true);
+
+  // Find the logical namespace for the 'Name' component.
+  LVScope *find(StringRef Name) {
+    LVScope *Namespace = (NamespaceNames.find(Name) != NamespaceNames.end())
+                             ? NamespaceNames[Name]
+                             : nullptr;
+    return Namespace;
+  }
+
+  // For the given lexical components, return a tuple with the first entry
+  // being the outermost namespace and the second entry being the first
+  // non-namespace.
+  LVLexicalIndex find(LVStringRefs Components) {
+    if (Components.empty())
+      return {};
+
+    LVStringRefs::size_type FirstNamespace = 0;
+    LVStringRefs::size_type FirstNonNamespace;
+    for (LVStringRefs::size_type Index = 0; Index < Components.size();
+         ++Index) {
+      FirstNonNamespace = Index;
+      LookupSet::iterator Iter = IdentifiedNamespaces.find(Components[Index]);
+      if (Iter == IdentifiedNamespaces.end())
+        // The component is not a namespace name.
+        break;
+    }
+    return std::make_tuple(FirstNamespace, FirstNonNamespace);
+  }
+};
+
+// Strings.
+class LVStringRecords {
+  using StringEntry = std::tuple<uint32_t, std::string, LVScopeCompileUnit *>;
+  using StringIds = std::map<TypeIndex, StringEntry>;
+  StringIds Strings;
+
+public:
+  LVStringRecords() = default;
+
+  void add(TypeIndex TI, StringRef String) {
+    static uint32_t Index = 0;
+    if (Strings.find(TI) == Strings.end())
+      Strings.emplace(
+          std::piecewise_construct, std::forward_as_tuple(TI),
+          std::forward_as_tuple(++Index, std::string(String), nullptr));
+  }
+
+  StringRef find(TypeIndex TI) {
+    StringIds::iterator Iter = Strings.find(TI);
+    return Iter != Strings.end() ? std::get<1>(Iter->second) : StringRef{};
+  }
+
+  uint32_t findIndex(TypeIndex TI) {
+    StringIds::iterator Iter = Strings.find(TI);
+    return Iter != Strings.end() ? std::get<0>(Iter->second) : 0;
+  }
+
+  // Move strings representing the filenames to the compile unit.
+  void addFilenames();
+  void addFilenames(LVScopeCompileUnit *Scope);
+};
+} // namespace
+
+using LVTypeKinds = std::set<TypeLeafKind>;
+using LVSymbolKinds = std::set<SymbolKind>;
+
+// The following data keeps forward information, type records, names for
+// namespace deduction, strings records, line records.
+// It is shared by the type visitor, symbol visitor and logical visitor and
+// it is independent from the CodeViewReader.
+struct LVShared {
+  LVCodeViewReader *Reader;
+  LVLogicalVisitor *Visitor;
+  LVForwardReferences ForwardReferences;
+  LVLineRecords LineRecords;
+  LVNamespaceDeduction NamespaceDeduction;
+  LVStringRecords StringRecords;
+  LVTypeRecords TypeRecords;
+
+  // In order to determine which types and/or symbols records should be handled
+  // by the reader, we record record kinds seen by the type and symbol visitors.
+  // At the end of the scopes creation, the '--internal=tag' option will allow
+  // to print the unique record ids collected.
+  LVTypeKinds TypeKinds;
+  LVSymbolKinds SymbolKinds;
+
+  LVShared(LVCodeViewReader *Reader, LVLogicalVisitor *Visitor)
+      : Reader(Reader), Visitor(Visitor), NamespaceDeduction(this),
+        TypeRecords(this) {}
+  ~LVShared() = default;
+};
+} // namespace logicalview
+} // namespace llvm
+
+void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind,
+                        LVElement *Element) {
+  RecordTable &Target =
+      (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds;
+  Target.emplace(std::piecewise_construct, std::forward_as_tuple(TI),
+                 std::forward_as_tuple(Kind, Element));
+}
+
+void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, StringRef Name) {
+  NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds;
+  Target.emplace(Name, TI);
+}
+
+LVElement *LVTypeRecords::find(uint32_t StreamIdx, TypeIndex TI, bool Create) {
+  RecordTable &Target =
+      (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds;
+
+  LVElement *Element = nullptr;
+  RecordTable::iterator Iter = Target.find(TI);
+  if (Iter != Target.end()) {
+    Element = Iter->second.second;
+    if (Element || !Create)
+      return Element;
+
+    // Create the logical element if not found.
+    Element = Shared->Visitor->createElement(Iter->second.first);
+    if (Element) {
+      Element->setOffset(TI.getIndex());
+      Element->setOffsetFromTypeIndex();
+      Target[TI].second = Element;
+    }
+  }
+  return Element;
+}
+
+TypeIndex LVTypeRecords::find(uint32_t StreamIdx, StringRef Name) {
+  NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds;
+  NameTable::iterator Iter = Target.find(Name);
+  return Iter != Target.end() ? Iter->second : TypeIndex::None();
+}
+
+void LVStringRecords::addFilenames() {
+  for (StringIds::const_reference Entry : Strings) {
+    StringRef Name = std::get<1>(Entry.second);
+    LVScopeCompileUnit *Scope = std::get<2>(Entry.second);
+    Scope->addFilename(transformPath(Name));
+  }
+  Strings.clear();
+}
+
+void LVStringRecords::addFilenames(LVScopeCompileUnit *Scope) {
+  for (StringIds::reference Entry : Strings)
+    if (!std::get<2>(Entry.second))
+      std::get<2>(Entry.second) = Scope;
+}
+
+void LVNamespaceDeduction::add(StringRef String) {
+  StringRef InnerComponent;
+  StringRef OuterComponent;
+  std::tie(OuterComponent, InnerComponent) = getInnerComponent(String);
+  DeducedScopes.insert(InnerComponent);
+  if (OuterComponent.size())
+    UnresolvedScopes.insert(OuterComponent);
+}
+
+void LVNamespaceDeduction::init() {
+  // We have 2 sets of names:
+  // - deduced scopes (class, structure, union and enum) and
+  // - unresolved scopes, that can represent namespaces or any deduced.
+  // Before creating the namespaces, we have to traverse the unresolved
+  // and remove any references to already deduced scopes.
+  LVStringRefs Components;
+  for (const StringRef &Unresolved : UnresolvedScopes) {
+    Components = getAllLexicalComponents(Unresolved);
+    for (const StringRef &Component : Components) {
+      LookupSet::iterator Iter = DeducedScopes.find(Component);
+      if (Iter == DeducedScopes.end())
+        IdentifiedNamespaces.insert(Component);
+    }
+  }
+
+  LLVM_DEBUG({
+    auto Print = [&](LookupSet &Container, const char *Title) {
+      auto Header = [&]() {
+        dbgs() << formatv("\n{0}\n", fmt_repeat('=', 72));
+        dbgs() << formatv("{0}\n", Title);
+        dbgs() << formatv("{0}\n", fmt_repeat('=', 72));
+      };
+      Header();
+      for (const StringRef &Item : Container)
+        dbgs() << formatv("'{0}'\n", Item.str().c_str());
+    };
+
+    Print(DeducedScopes, "Deducted Scopes");
+    Print(UnresolvedScopes, "Unresolved Scopes");
+    Print(IdentifiedNamespaces, "Namespaces");
+  });
+}
+
+LVScope *LVNamespaceDeduction::get(LVStringRefs Components) {
+  LLVM_DEBUG({
+    for (const StringRef &Component : Components)
+      dbgs() << formatv("'{0}'\n", Component.str().c_str());
+  });
+
+  if (Components.empty())
+    return nullptr;
+
+  // Update the namespaces relationship.
+  LVScope *Namespace = nullptr;
+  LVScope *Parent = Shared->Reader->getCompileUnit();
+  for (const StringRef &Component : Components) {
+    // Check if we have seen the namespace.
+    Namespace = find(Component);
+    if (!Namespace) {
+      // We have identified namespaces that are generated by MSVC. Mark them
+      // as 'system' so they will be excluded from the logical view.
+      Namespace = Shared->Reader->createScopeNamespace();
+      Namespace->setTag(dwarf::DW_TAG_namespace);
+      Namespace->setName(Component);
+      Parent->addElement(Namespace);
+      getReader().isSystemEntry(Namespace);
+      add(Component, Namespace);
+    }
+    Parent = Namespace;
+  }
+  return Parent;
+}
+
+LVScope *LVNamespaceDeduction::get(StringRef ScopedName, bool CheckScope) {
+  LVStringRefs Components = getAllLexicalComponents(ScopedName);
+  if (CheckScope)
+    Components.erase(std::remove_if(Components.begin(), Components.end(),
+                                    [&](StringRef Component) {
+                                      LookupSet::iterator Iter =
+                                          IdentifiedNamespaces.find(Component);
+                                      return Iter == IdentifiedNamespaces.end();
+                                    }),
+                     Components.end());
+
+  LLVM_DEBUG(
+      { dbgs() << formatv("ScopedName: '{0}'\n", ScopedName.str().c_str()); });
+
+  return get(Components);
+}
+
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "CodeViewTypeVisitor"
+
+//===----------------------------------------------------------------------===//
+// TypeRecord traversal.
+//===----------------------------------------------------------------------===//
+void LVTypeVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI,
+                                   uint32_t StreamIdx) const {
+  codeview::printTypeIndex(W, FieldName, TI,
+                           StreamIdx == StreamTPI ? Types : Ids);
+}
+
+Error LVTypeVisitor::visitTypeBegin(CVType &Record) {
+  return visitTypeBegin(Record, TypeIndex::fromArrayIndex(Types.size()));
+}
+
+Error LVTypeVisitor::visitTypeBegin(CVType &Record, TypeIndex TI) {
+  LLVM_DEBUG({
+    W.getOStream() << formatTypeLeafKind(Record.kind());
+    W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")\n";
+  });
+
+  if (options().getInternalTag())
+    Shared->TypeKinds.insert(Record.kind());
+
+  // The collected type records, will be use to create the logical elements
+  // during the symbols traversal when a type is referenced.
+  CurrentTypeIndex = TI;
+  Shared->TypeRecords.add(StreamIdx, TI, Record.kind());
+  return Error::success();
+}
+
+Error LVTypeVisitor::visitUnknownType(CVType &Record) {
+  LLVM_DEBUG({ W.printNumber("Length", uint32_t(Record.content().size())); });
+  return Error::success();
+}
+
+Error LVTypeVisitor::visitMemberBegin(CVMemberRecord &Record) {
+  LLVM_DEBUG({
+    W.startLine() << formatTypeLeafKind(Record.Kind);
+    W.getOStream() << " {\n";
+    W.indent();
+  });
+  return Error::success();
+}
+
+Error LVTypeVisitor::visitMemberEnd(CVMemberRecord &Record) {
+  LLVM_DEBUG({
+    W.unindent();
+    W.startLine() << "}\n";
+  });
+  return Error::success();
+}
+
+Error LVTypeVisitor::visitUnknownMember(CVMemberRecord &Record) {
+  LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); });
+  return Error::success();
+}
+
+// LF_BUILDINFO (TPI)/(IPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &Args) {
+  // All the args are references into the TPI/IPI stream.
+  LLVM_DEBUG({
+    W.printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size()));
+    ListScope Arguments(W, "Arguments");
+    for (TypeIndex Arg : Args.getArgs())
+      printTypeIndex("ArgType", Arg, StreamIPI);
+  });
+
+  // Only add the strings that hold information about filenames. They will be
+  // used to complete the line/file information for the logical elements.
+  // There are other strings holding information about namespaces.
+  TypeIndex TI;
+  StringRef String;
+
+  // Absolute CWD path
+  TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::CurrentDirectory];
+  String = Ids.getTypeName(TI);
+  if (!String.empty())
+    Shared->StringRecords.add(TI, String);
+
+  // Get the compile unit name.
+  TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile];
+  String = Ids.getTypeName(TI);
+  if (!String.empty())
+    Shared->StringRecords.add(TI, String);
+  LogicalVisitor->setCompileUnitName(std::string(String));
+
+  return Error::success();
+}
+
+// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class) {
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI);
+    printTypeIndex("FieldListType", Class.getFieldList(), StreamTPI);
+    W.printString("Name", Class.getName());
+  });
+
+  // Collect class name for scope deduction.
+  Shared->NamespaceDeduction.add(Class.getName());
+  Shared->ForwardReferences.record(Class.isForwardRef(), Class.getName(),
+                                   CurrentTypeIndex);
+
+  // Collect class name for contained scopes deduction.
+  Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Class.getName());
+  return Error::success();
+}
+
+// LF_ENUM (TPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum) {
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI);
+    printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI);
+    W.printString("Name", Enum.getName());
+  });
+
+  // Collect enum name for scope deduction.
+  Shared->NamespaceDeduction.add(Enum.getName());
+  return Error::success();
+}
+
+// LF_FUNC_ID (TPI)/(IPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func) {
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI);
+    printTypeIndex("Type", Func.getFunctionType(), StreamTPI);
+    printTypeIndex("Parent", Func.getParentScope(), StreamTPI);
+    W.printString("Name", Func.getName());
+  });
+
+  // Collect function name for scope deduction.
+  Shared->NamespaceDeduction.add(Func.getName());
+  return Error::success();
+}
+
+// LF_PROCEDURE (TPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc) {
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI);
+    printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI);
+    W.printNumber("NumParameters", Proc.getParameterCount());
+    printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI);
+  });
+
+  // Collect procedure information as they can be referenced by typedefs.
+  Shared->TypeRecords.add(StreamTPI, CurrentTypeIndex, {});
+  return Error::success();
+}
+
+// LF_STRING_ID (TPI)/(IPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String) {
+  // No additional references are needed.
+  LLVM_DEBUG({
+    printTypeIndex("Id", String.getId(), StreamIPI);
+    W.printString("StringData", String.getString());
+  });
+  return Error::success();
+}
+
+// LF_UDT_SRC_LINE (TPI)/(IPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record,
+                                      UdtSourceLineRecord &Line) {
+  // UDT and SourceFile are references into the TPI/IPI stream.
+  LLVM_DEBUG({
+    printTypeIndex("UDT", Line.getUDT(), StreamIPI);
+    printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI);
+    W.printNumber("LineNumber", Line.getLineNumber());
+  });
+
+  Shared->LineRecords.push_back(CurrentTypeIndex);
+  return Error::success();
+}
+
+// LF_UNION (TPI)
+Error LVTypeVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union) {
+  LLVM_DEBUG({
+    W.printNumber("MemberCount", Union.getMemberCount());
+    printTypeIndex("FieldList", Union.getFieldList(), StreamTPI);
+    W.printNumber("SizeOf", Union.getSize());
+    W.printString("Name", Union.getName());
+    if (Union.hasUniqueName())
+      W.printString("UniqueName", Union.getUniqueName());
+  });
+
+  // Collect union name for scope deduction.
+  Shared->NamespaceDeduction.add(Union.getName());
+  Shared->ForwardReferences.record(Union.isForwardRef(), Union.getName(),
+                                   CurrentTypeIndex);
+
+  // Collect class name for contained scopes deduction.
+  Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Union.getName());
+  return Error::success();
+}
+
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "CodeViewSymbolVisitor"
+
+//===----------------------------------------------------------------------===//
+// SymbolRecord traversal.
+//===----------------------------------------------------------------------===//
+void LVSymbolVisitorDelegate::printRelocatedField(StringRef Label,
+                                                  uint32_t RelocOffset,
+                                                  uint32_t Offset,
+                                                  StringRef *RelocSym) {
+  Reader->printRelocatedField(Label, CoffSection, RelocOffset, Offset,
+                              RelocSym);
+}
+
+void LVSymbolVisitorDelegate::getLinkageName(uint32_t RelocOffset,
+                                             uint32_t Offset,
+                                             StringRef *RelocSym) {
+  Reader->getLinkageName(CoffSection, RelocOffset, Offset, RelocSym);
+}
+
+StringRef
+LVSymbolVisitorDelegate::getFileNameForFileOffset(uint32_t FileOffset) {
+  Expected<StringRef> Name = Reader->getFileNameForFileOffset(FileOffset);
+  if (!Name) {
+    consumeError(Name.takeError());
+    return {};
+  }
+  return *Name;
+}
+
+DebugStringTableSubsectionRef LVSymbolVisitorDelegate::getStringTable() {
+  return Reader->CVStringTable;
+}
+
+void LVSymbolVisitor::printLocalVariableAddrRange(
+    const LocalVariableAddrRange &Range, uint32_t RelocationOffset) {
+  DictScope S(W, "LocalVariableAddrRange");
+  if (ObjDelegate)
+    ObjDelegate->printRelocatedField("OffsetStart", RelocationOffset,
+                                     Range.OffsetStart);
+  W.printHex("ISectStart", Range.ISectStart);
+  W.printHex("Range", Range.Range);
+}
+
+void LVSymbolVisitor::printLocalVariableAddrGap(
+    ArrayRef<LocalVariableAddrGap> Gaps) {
+  for (const LocalVariableAddrGap &Gap : Gaps) {
+    ListScope S(W, "LocalVariableAddrGap");
+    W.printHex("GapStartOffset", Gap.GapStartOffset);
+    W.printHex("Range", Gap.Range);
+  }
+}
+
+void LVSymbolVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const {
+  codeview::printTypeIndex(W, FieldName, TI, Types);
+}
+
+Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record) {
+  return visitSymbolBegin(Record, 0);
+}
+
+Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record, uint32_t Offset) {
+  SymbolKind Kind = Record.kind();
+  LLVM_DEBUG({
+    W.printNumber("Offset", Offset);
+    W.printEnum("Begin Kind", unsigned(Kind), getSymbolTypeNames());
+  });
+
+  if (options().getInternalTag())
+    Shared->SymbolKinds.insert(Kind);
+
+  LogicalVisitor->CurrentElement = LogicalVisitor->createElement(Kind);
+  if (!LogicalVisitor->CurrentElement) {
+    LLVM_DEBUG({
+        // We have an unsupported Symbol or Type Record.
+        // W.printEnum("Kind ignored", unsigned(Kind), getSymbolTypeNames());
+    });
+    return Error::success();
+  }
+
+  // Offset carried by the traversal routines when dealing with streams.
+  CurrentOffset = Offset;
+  IsCompileUnit = false;
+  if (!LogicalVisitor->CurrentElement->getOffsetFromTypeIndex())
+    LogicalVisitor->CurrentElement->setOffset(Offset);
+  if (symbolOpensScope(Kind) || (IsCompileUnit = symbolIsCompileUnit(Kind))) {
+    assert(LogicalVisitor->CurrentScope && "Invalid scope!");
+    LogicalVisitor->addElement(LogicalVisitor->CurrentScope, IsCompileUnit);
+  } else {
+    if (LogicalVisitor->CurrentSymbol)
+      LogicalVisitor->addElement(LogicalVisitor->CurrentSymbol);
+    if (LogicalVisitor->CurrentType)
+      LogicalVisitor->addElement(LogicalVisitor->CurrentType);
+  }
+
+  return Error::success();
+}
+
+Error LVSymbolVisitor::visitSymbolEnd(CVSymbol &Record) {
+  SymbolKind Kind = Record.kind();
+  LLVM_DEBUG(
+      { W.printEnum("End Kind", unsigned(Kind), getSymbolTypeNames()); });
+
+  if (symbolEndsScope(Kind)) {
+    LogicalVisitor->popScope();
+  }
+
+  return Error::success();
+}
+
+Error LVSymbolVisitor::visitUnknownSymbol(CVSymbol &Record) {
+  LLVM_DEBUG({ W.printNumber("Length", Record.length()); });
+  return Error::success();
+}
+
+// S_BLOCK32
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, BlockSym &Block) {
+  LLVM_DEBUG({
+    W.printHex("CodeSize", Block.CodeSize);
+    W.printHex("Segment", Block.Segment);
+    W.printString("BlockName", Block.Name);
+  });
+
+  if (LVScope *Scope = LogicalVisitor->CurrentScope) {
+    StringRef LinkageName;
+    if (ObjDelegate)
+      ObjDelegate->getLinkageName(Block.getRelocationOffset(), Block.CodeOffset,
+                                  &LinkageName);
+    Scope->setLinkageName(LinkageName);
+
+    if (options().getGeneralCollectRanges()) {
+      // Record converted segment::offset addressing for this scope.
+      LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName);
+      LVAddress LowPC =
+          Reader->linearAddress(Block.Segment, Block.CodeOffset, Addendum);
+      LVAddress HighPC = LowPC + Block.CodeSize - 1;
+      Scope->addObject(LowPC, HighPC);
+    }
+  }
+
+  return Error::success();
+}
+
+// S_BPREL32
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        BPRelativeSym &Local) {
+  LLVM_DEBUG({
+    printTypeIndex("Type", Local.Type);
+    W.printNumber("Offset", Local.Offset);
+    W.printString("VarName", Local.Name);
+  });
+
+  if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) {
+    Symbol->setName(Local.Name);
+    // From the MS_Symbol_Type.pdf documentation (S_BPREL32):
+    // This symbol specifies symbols that are allocated on the stack for a
+    // procedure. For C and C++, these include the actual function parameters
+    // and the local non-static variables of functions.
+    // However, the offset for 'this' comes as a negative value.
+
+    // Symbol was created as 'variable'; determine its real kind.
+    Symbol->resetIsVariable();
+
+    if (Local.Name.equals("this")) {
+      Symbol->setIsParameter();
+      Symbol->setIsArtificial();
+    } else {
+      // Determine symbol kind.
+      bool(Local.Offset > 0) ? Symbol->setIsParameter()
+                             : Symbol->setIsVariable();
+    }
+
+    // Update correct debug information tag.
+    if (Symbol->getIsParameter())
+      Symbol->setTag(dwarf::DW_TAG_formal_parameter);
+
+    LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type);
+    if (Element && Element->getIsScoped()) {
+      // We have a local type. Find its parent function.
+      LVScope *Parent = Symbol->getFunctionParent();
+      // The element representing the type has been already finalized. If
+      // the type is an aggregate type, its members have been already added.
+      // As the type is local, its level will be changed.
+
+      // FIXME: Currently the algorithm used to scope lambda functions is
+      // incorrect. Before we allocate the type at this scope, check if is
+      // already allocated in other scope.
+      if (!Element->getParentScope()) {
+        Parent->addElement(Element);
+        Element->updateLevel(Parent);
+      }
+    }
+    Symbol->setType(Element);
+  }
+
+  return Error::success();
+}
+
+// S_REGREL32
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        RegRelativeSym &Local) {
+  LLVM_DEBUG({
+    printTypeIndex("Type", Local.Type);
+    W.printNumber("Offset", Local.Offset);
+    W.printString("VarName", Local.Name);
+  });
+
+  if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) {
+    Symbol->setName(Local.Name);
+
+    // Symbol was created as 'variable'; determine its real kind.
+    Symbol->resetIsVariable();
+
+    // Check for the 'this' symbol.
+    if (Local.Name.equals("this")) {
+      Symbol->setIsArtificial();
+      Symbol->setIsParameter();
+    } else {
+      // Determine symbol kind.
+      determineSymbolKind(Symbol, Local.Register);
+    }
+
+    // Update correct debug information tag.
+    if (Symbol->getIsParameter())
+      Symbol->setTag(dwarf::DW_TAG_formal_parameter);
+
+    LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type);
+    if (Element && Element->getIsScoped()) {
+      // We have a local type. Find its parent function.
+      LVScope *Parent = Symbol->getFunctionParent();
+      // The element representing the type has been already finalized. If
+      // the type is an aggregate type, its members have been already added.
+      // As the type is local, its level will be changed.
+
+      // FIXME: Currently the algorithm used to scope lambda functions is
+      // incorrect. Before we allocate the type at this scope, check if is
+      // already allocated in other scope.
+      if (!Element->getParentScope()) {
+        Parent->addElement(Element);
+        Element->updateLevel(Parent);
+      }
+    }
+    Symbol->setType(Element);
+  }
+
+  return Error::success();
+}
+
+// S_BUILDINFO
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR,
+                                        BuildInfoSym &BuildInfo) {
+  LLVM_DEBUG({ printTypeIndex("BuildId", BuildInfo.BuildId); });
+
+  CVType CVBuildType = Ids.getType(BuildInfo.BuildId);
+  if (Error Err = LogicalVisitor->finishVisitation(
+          CVBuildType, BuildInfo.BuildId, Reader->getCompileUnit()))
+    return Err;
+
+  return Error::success();
+}
+
+// S_COMPILE2
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        Compile2Sym &Compile2) {
+  LLVM_DEBUG({
+    W.printEnum("Language", uint8_t(Compile2.getLanguage()),
+                getSourceLanguageNames());
+    W.printFlags("Flags", uint32_t(Compile2.getFlags()),
+                 getCompileSym3FlagNames());
+    W.printEnum("Machine", unsigned(Compile2.Machine), getCPUTypeNames());
+    W.printString("VersionName", Compile2.Version);
+  });
+
+  // MSVC generates the following sequence for a CodeView module:
+  //   S_OBJNAME    --> Set 'CurrentObjectName'.
+  //   S_COMPILE2   --> Set the compile unit name using 'CurrentObjectName'.
+  //   ...
+  //   S_BUILDINFO  --> Extract the source name.
+  //
+  // Clang generates the following sequence for a CodeView module:
+  //   S_COMPILE2   --> Set the compile unit name to empty string.
+  //   ...
+  //   S_BUILDINFO  --> Extract the source name.
+  //
+  // For both toolchains, update the compile unit name from S_BUILDINFO.
+  if (LVScope *Scope = LogicalVisitor->CurrentScope) {
+    // The name of the CU, was extracted from the 'BuildInfo' subsection.
+    Reader->setCompileUnitCPUType(Compile2.Machine);
+    Scope->setName(CurrentObjectName);
+    if (options().getAttributeProducer())
+      Scope->setProducer(Compile2.Version);
+    getReader().isSystemEntry(Scope, CurrentObjectName);
+
+    // The line records in CodeView are recorded per Module ID. Update
+    // the relationship between the current CU and the Module ID.
+    Reader->addModule(Scope);
+
+    // Updated the collected strings with their associated compile unit.
+    Shared->StringRecords.addFilenames(Reader->getCompileUnit());
+  }
+
+  // Clear any previous ObjectName.
+  CurrentObjectName = "";
+  return Error::success();
+}
+
+// S_COMPILE3
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        Compile3Sym &Compile3) {
+  LLVM_DEBUG({
+    W.printEnum("Language", uint8_t(Compile3.getLanguage()),
+                getSourceLanguageNames());
+    W.printFlags("Flags", uint32_t(Compile3.getFlags()),
+                 getCompileSym3FlagNames());
+    W.printEnum("Machine", unsigned(Compile3.Machine), getCPUTypeNames());
+    W.printString("VersionName", Compile3.Version);
+  });
+
+  // MSVC generates the following sequence for a CodeView module:
+  //   S_OBJNAME    --> Set 'CurrentObjectName'.
+  //   S_COMPILE3   --> Set the compile unit name using 'CurrentObjectName'.
+  //   ...
+  //   S_BUILDINFO  --> Extract the source name.
+  //
+  // Clang generates the following sequence for a CodeView module:
+  //   S_COMPILE3   --> Set the compile unit name to empty string.
+  //   ...
+  //   S_BUILDINFO  --> Extract the source name.
+  //
+  // For both toolchains, update the compile unit name from S_BUILDINFO.
+  if (LVScope *Scope = LogicalVisitor->CurrentScope) {
+    // The name of the CU, was extracted from the 'BuildInfo' subsection.
+    Reader->setCompileUnitCPUType(Compile3.Machine);
+    Scope->setName(CurrentObjectName);
+    if (options().getAttributeProducer())
+      Scope->setProducer(Compile3.Version);
+    getReader().isSystemEntry(Scope, CurrentObjectName);
+
+    // The line records in CodeView are recorded per Module ID. Update
+    // the relationship between the current CU and the Module ID.
+    Reader->addModule(Scope);
+
+    // Updated the collected strings with their associated compile unit.
+    Shared->StringRecords.addFilenames(Reader->getCompileUnit());
+  }
+
+  // Clear any previous ObjectName.
+  CurrentObjectName = "";
+  return Error::success();
+}
+
+// S_CONSTANT, S_MANCONSTANT
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        ConstantSym &Constant) {
+  LLVM_DEBUG({
+    printTypeIndex("Type", Constant.Type);
+    W.printNumber("Value", Constant.Value);
+    W.printString("Name", Constant.Name);
+  });
+
+  if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) {
+    Symbol->setName(Constant.Name);
+    Symbol->setType(LogicalVisitor->getElement(StreamTPI, Constant.Type));
+    Symbol->resetIncludeInPrint();
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE
+Error LVSymbolVisitor::visitKnownRecord(
+    CVSymbol &Record,
+    DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    W.printNumber("Offset", DefRangeFramePointerRelFullScope.Offset);
+  });
+
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location. Operands: [Offset, 0].
+    dwarf::Attribute Attr =
+        dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE);
+
+    uint64_t Operand1 = DefRangeFramePointerRelFullScope.Offset;
+    Symbol->addLocation(Attr, 0, 0, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0);
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE_FRAMEPOINTER_REL
+Error LVSymbolVisitor::visitKnownRecord(
+    CVSymbol &Record, DefRangeFramePointerRelSym &DefRangeFramePointerRel) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    W.printNumber("Offset", DefRangeFramePointerRel.Hdr.Offset);
+    printLocalVariableAddrRange(DefRangeFramePointerRel.Range,
+                                DefRangeFramePointerRel.getRelocationOffset());
+    printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps);
+  });
+
+  // We are expecting the following sequence:
+  //   128 | S_LOCAL [size = 20] `ParamBar`
+  //         ...
+  //   148 | S_DEFRANGE_FRAMEPOINTER_REL [size = 16]
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location. Operands: [Offset, 0].
+    dwarf::Attribute Attr =
+        dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL);
+    uint64_t Operand1 = DefRangeFramePointerRel.Hdr.Offset;
+
+    LocalVariableAddrRange Range = DefRangeFramePointerRel.Range;
+    LVAddress Address =
+        Reader->linearAddress(Range.ISectStart, Range.OffsetStart);
+
+    Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0);
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE_REGISTER_REL
+Error LVSymbolVisitor::visitKnownRecord(
+    CVSymbol &Record, DefRangeRegisterRelSym &DefRangeRegisterRel) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    W.printBoolean("HasSpilledUDTMember",
+                   DefRangeRegisterRel.hasSpilledUDTMember());
+    W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent());
+    W.printNumber("BasePointerOffset",
+                  DefRangeRegisterRel.Hdr.BasePointerOffset);
+    printLocalVariableAddrRange(DefRangeRegisterRel.Range,
+                                DefRangeRegisterRel.getRelocationOffset());
+    printLocalVariableAddrGap(DefRangeRegisterRel.Gaps);
+  });
+
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location. Operands: [Register, Offset].
+    dwarf::Attribute Attr =
+        dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER_REL);
+    uint64_t Operand1 = DefRangeRegisterRel.Hdr.Register;
+    uint64_t Operand2 = DefRangeRegisterRel.Hdr.BasePointerOffset;
+
+    LocalVariableAddrRange Range = DefRangeRegisterRel.Range;
+    LVAddress Address =
+        Reader->linearAddress(Range.ISectStart, Range.OffsetStart);
+
+    Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, Operand2);
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE_REGISTER
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        DefRangeRegisterSym &DefRangeRegister) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register),
+                getRegisterNames(Reader->getCompileUnitCPUType()));
+    W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName);
+    printLocalVariableAddrRange(DefRangeRegister.Range,
+                                DefRangeRegister.getRelocationOffset());
+    printLocalVariableAddrGap(DefRangeRegister.Gaps);
+  });
+
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location. Operands: [Register, 0].
+    dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER);
+    uint64_t Operand1 = DefRangeRegister.Hdr.Register;
+
+    LocalVariableAddrRange Range = DefRangeRegister.Range;
+    LVAddress Address =
+        Reader->linearAddress(Range.ISectStart, Range.OffsetStart);
+
+    Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0);
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE_SUBFIELD_REGISTER
+Error LVSymbolVisitor::visitKnownRecord(
+    CVSymbol &Record, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register),
+                getRegisterNames(Reader->getCompileUnitCPUType()));
+    W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName);
+    W.printNumber("OffsetInParent",
+                  DefRangeSubfieldRegister.Hdr.OffsetInParent);
+    printLocalVariableAddrRange(DefRangeSubfieldRegister.Range,
+                                DefRangeSubfieldRegister.getRelocationOffset());
+    printLocalVariableAddrGap(DefRangeSubfieldRegister.Gaps);
+  });
+
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location.  Operands: [Register, 0].
+    dwarf::Attribute Attr =
+        dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER);
+    uint64_t Operand1 = DefRangeSubfieldRegister.Hdr.Register;
+
+    LocalVariableAddrRange Range = DefRangeSubfieldRegister.Range;
+    LVAddress Address =
+        Reader->linearAddress(Range.ISectStart, Range.OffsetStart);
+
+    Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0);
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE_SUBFIELD
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        DefRangeSubfieldSym &DefRangeSubfield) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    if (ObjDelegate) {
+      DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable();
+      auto ExpectedProgram = Strings.getString(DefRangeSubfield.Program);
+      if (!ExpectedProgram) {
+        consumeError(ExpectedProgram.takeError());
+        return llvm::make_error<CodeViewError>(
+            "String table offset outside of bounds of String Table!");
+      }
+      W.printString("Program", *ExpectedProgram);
+    }
+    W.printNumber("OffsetInParent", DefRangeSubfield.OffsetInParent);
+    printLocalVariableAddrRange(DefRangeSubfield.Range,
+                                DefRangeSubfield.getRelocationOffset());
+    printLocalVariableAddrGap(DefRangeSubfield.Gaps);
+  });
+
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location. Operands: [Program, 0].
+    dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD);
+    uint64_t Operand1 = DefRangeSubfield.Program;
+
+    LocalVariableAddrRange Range = DefRangeSubfield.Range;
+    LVAddress Address =
+        Reader->linearAddress(Range.ISectStart, Range.OffsetStart);
+
+    Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0);
+  }
+
+  return Error::success();
+}
+
+// S_DEFRANGE
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        DefRangeSym &DefRange) {
+  // DefRanges don't have types, just registers and code offsets.
+  LLVM_DEBUG({
+    if (LocalSymbol)
+      W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName());
+
+    if (ObjDelegate) {
+      DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable();
+      auto ExpectedProgram = Strings.getString(DefRange.Program);
+      if (!ExpectedProgram) {
+        consumeError(ExpectedProgram.takeError());
+        return llvm::make_error<CodeViewError>(
+            "String table offset outside of bounds of String Table!");
+      }
+      W.printString("Program", *ExpectedProgram);
+    }
+    printLocalVariableAddrRange(DefRange.Range, DefRange.getRelocationOffset());
+    printLocalVariableAddrGap(DefRange.Gaps);
+  });
+
+  if (LVSymbol *Symbol = LocalSymbol) {
+    Symbol->setHasCodeViewLocation();
+    LocalSymbol = nullptr;
+
+    // Add location debug location. Operands: [Program, 0].
+    dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE);
+    uint64_t Operand1 = DefRange.Program;
+
+    LocalVariableAddrRange Range = DefRange.Range;
+    LVAddress Address =
+        Reader->linearAddress(Range.ISectStart, Range.OffsetStart);
+
+    Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0);
+    Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0);
+  }
+
+  return Error::success();
+}
+
+// S_FRAMEPROC
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        FrameProcSym &FrameProc) {
+  if (LVScope *Function = LogicalVisitor->getReaderScope()) {
+    // S_FRAMEPROC contains extra information for the function described
+    // by any of the previous generated records:
+    // S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID.
+
+    // The generated sequence is:
+    //   S_GPROC32_ID ...
+    //   S_FRAMEPROC ...
+
+    // Collect additional inline flags for the current scope function.
+    FrameProcedureOptions Flags = FrameProc.Flags;
+    if (FrameProcedureOptions::MarkedInline ==
+        (Flags & FrameProcedureOptions::MarkedInline))
+      Function->setInlineCode(dwarf::DW_INL_declared_inlined);
+    if (FrameProcedureOptions::Inlined ==
+        (Flags & FrameProcedureOptions::Inlined))
+      Function->setInlineCode(dwarf::DW_INL_inlined);
+
+    // To determine the symbol kind for any symbol declared in that function,
+    // we can access the S_FRAMEPROC for the parent scope function. It contains
+    // information about the local fp and param fp registers and compare with
+    // the register in the S_REGREL32 to get a match.
+    codeview::CPUType CPU = Reader->getCompileUnitCPUType();
+    LocalFrameRegister = FrameProc.getLocalFramePtrReg(CPU);
+    ParamFrameRegister = FrameProc.getParamFramePtrReg(CPU);
+  }
+
+  return Error::success();
+}
+
+// S_GDATA32, S_LDATA32, S_LMANDATA, S_GMANDATA
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, DataSym &Data) {
+  LLVM_DEBUG({
+    printTypeIndex("Type", Data.Type);
+    W.printString("DisplayName", Data.Name);
+  });
+
+  if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) {
+    StringRef LinkageName;
+    if (ObjDelegate)
+      ObjDelegate->getLinkageName(Data.getRelocationOffset(), Data.DataOffset,
+                                  &LinkageName);
+
+    Symbol->setName(Data.Name);
+    Symbol->setLinkageName(LinkageName);
+
+    // The MSVC generates local data as initialization for aggregates. It
+    // contains the address for an initialization function.
+    // The symbols contains the '$initializer$' pattern. Allow them only if
+    // the '--internal=system' option is given.
+    //   0 | S_LDATA32 `Struct$initializer$`
+    //       type = 0x1040 (void ()*)
+    if (getReader().isSystemEntry(Symbol) && !options().getAttributeSystem()) {
+      Symbol->resetIncludeInPrint();
+      return Error::success();
+    }
+
+    if (LVScope *Namespace = Shared->NamespaceDeduction.get(Data.Name)) {
+      // The variable is already at 
diff erent scope. In order to reflect
+      // the correct parent, move it to the namespace.
+      if (Symbol->getParentScope()->removeElement(Symbol))
+        Namespace->addElement(Symbol);
+    }
+
+    Symbol->setType(LogicalVisitor->getElement(StreamTPI, Data.Type));
+    if (Record.kind() == SymbolKind::S_GDATA32)
+      Symbol->setIsExternal();
+  }
+
+  return Error::success();
+}
+
+// S_INLINESITE
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        InlineSiteSym &InlineSite) {
+  LLVM_DEBUG({ printTypeIndex("Inlinee", InlineSite.Inlinee); });
+
+  if (LVScope *InlinedFunction = LogicalVisitor->CurrentScope) {
+    LVScope *AbstractFunction = Reader->createScopeFunction();
+    AbstractFunction->setIsSubprogram();
+    AbstractFunction->setTag(dwarf::DW_TAG_subprogram);
+    AbstractFunction->setInlineCode(dwarf::DW_INL_inlined);
+    AbstractFunction->setIsInlinedAbstract();
+    InlinedFunction->setReference(AbstractFunction);
+
+    LogicalVisitor->startProcessArgumentList();
+    // 'Inlinee' is a Type ID.
+    CVType CVFunctionType = Ids.getType(InlineSite.Inlinee);
+    if (Error Err = LogicalVisitor->finishVisitation(
+            CVFunctionType, InlineSite.Inlinee, AbstractFunction))
+      return Err;
+    LogicalVisitor->stopProcessArgumentList();
+
+    // For inlined functions set the linkage name to be the same as
+    // the name. It used to find their lines and ranges.
+    StringRef Name = AbstractFunction->getName();
+    InlinedFunction->setName(Name);
+    InlinedFunction->setLinkageName(Name);
+
+    // Process annotation bytes to calculate code and line offsets.
+    if (Error Err = LogicalVisitor->inlineSiteAnnotation(
+            AbstractFunction, InlinedFunction, InlineSite))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+// S_LOCAL
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, LocalSym &Local) {
+  LLVM_DEBUG({
+    printTypeIndex("Type", Local.Type);
+    W.printFlags("Flags", uint16_t(Local.Flags), getLocalFlagNames());
+    W.printString("VarName", Local.Name);
+  });
+
+  if (LVSymbol *Symbol = LogicalVisitor->CurrentSymbol) {
+    Symbol->setName(Local.Name);
+
+    // Symbol was created as 'variable'; determine its real kind.
+    Symbol->resetIsVariable();
+
+    // Be sure the 'this' symbol is marked as 'compiler generated'.
+    if (bool(Local.Flags & LocalSymFlags::IsCompilerGenerated) ||
+        Local.Name.equals("this")) {
+      Symbol->setIsArtificial();
+      Symbol->setIsParameter();
+    } else {
+      bool(Local.Flags & LocalSymFlags::IsParameter) ? Symbol->setIsParameter()
+                                                     : Symbol->setIsVariable();
+    }
+
+    // Update correct debug information tag.
+    if (Symbol->getIsParameter())
+      Symbol->setTag(dwarf::DW_TAG_formal_parameter);
+
+    LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type);
+    if (Element && Element->getIsScoped()) {
+      // We have a local type. Find its parent function.
+      LVScope *Parent = Symbol->getFunctionParent();
+      // The element representing the type has been already finalized. If
+      // the type is an aggregate type, its members have been already added.
+      // As the type is local, its level will be changed.
+      Parent->addElement(Element);
+      Element->updateLevel(Parent);
+    }
+    Symbol->setType(Element);
+
+    // The CodeView records (S_DEFFRAME_*) describing debug location for
+    // this symbol, do not have any direct reference to it. Those records
+    // are emitted after this symbol. Record the current symbol.
+    LocalSymbol = Symbol;
+  }
+
+  return Error::success();
+}
+
+// S_OBJNAME
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) {
+  LLVM_DEBUG({
+    W.printHex("Signature", ObjName.Signature);
+    W.printString("ObjectName", ObjName.Name);
+  });
+
+  CurrentObjectName = ObjName.Name;
+  return Error::success();
+}
+
+// S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ProcSym &Proc) {
+  if (InFunctionScope)
+    return llvm::make_error<CodeViewError>("Visiting a ProcSym while inside "
+                                           "function scope!");
+
+  InFunctionScope = true;
+
+  LLVM_DEBUG({
+    printTypeIndex("FunctionType", Proc.FunctionType);
+    W.printHex("Segment", Proc.Segment);
+    W.printFlags("Flags", static_cast<uint8_t>(Proc.Flags),
+                 getProcSymFlagNames());
+    W.printString("DisplayName", Proc.Name);
+  });
+
+  // Clang and Microsoft generated 
diff erent debug information records:
+  // For functions definitions:
+  // Clang:     S_GPROC32 -> LF_FUNC_ID -> LF_PROCEDURE
+  // Microsoft: S_GPROC32 ->               LF_PROCEDURE
+
+  // For member function definition:
+  // Clang:     S_GPROC32 -> LF_MFUNC_ID -> LF_MFUNCTION
+  // Microsoft: S_GPROC32 ->                LF_MFUNCTION
+  // In order to support both sequences, if we found LF_FUNCTION_ID, just
+  // get the TypeIndex for LF_PROCEDURE.
+
+  // For the given test case, we have the sequence:
+  // namespace NSP_local {
+  //   void foo_local() {
+  //   }
+  // }
+  //
+  // 0x1000 | LF_STRING_ID String: NSP_local
+  // 0x1002 | LF_PROCEDURE
+  //          return type = 0x0003 (void), # args = 0, param list = 0x1001
+  //          calling conv = cdecl, options = None
+  // 0x1003 | LF_FUNC_ID
+  //          name = foo_local, type = 0x1002, parent scope = 0x1000
+  //      0 | S_GPROC32_ID `NSP_local::foo_local`
+  //          type = `0x1003 (foo_local)`
+  // 0x1004 | LF_STRING_ID String: suite
+  // 0x1005 | LF_STRING_ID String: suite_local.cpp
+  //
+  // The LF_STRING_ID can hold 
diff erent information:
+  // 0x1000 - The enclosing namespace.
+  // 0x1004 - The compile unit directory name.
+  // 0x1005 - The compile unit name.
+  //
+  // Before deducting its scope, we need to evaluate its type and create any
+  // associated namespaces.
+  if (LVScope *Function = LogicalVisitor->CurrentScope) {
+    StringRef LinkageName;
+    if (ObjDelegate)
+      ObjDelegate->getLinkageName(Proc.getRelocationOffset(), Proc.CodeOffset,
+                                  &LinkageName);
+
+    // The line table can be accessed using the linkage name.
+    Reader->addToSymbolTable(LinkageName, Function);
+    Function->setName(Proc.Name);
+    Function->setLinkageName(LinkageName);
+
+    if (options().getGeneralCollectRanges()) {
+      // Record converted segment::offset addressing for this scope.
+      LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName);
+      LVAddress LowPC =
+          Reader->linearAddress(Proc.Segment, Proc.CodeOffset, Addendum);
+      LVAddress HighPC = LowPC + Proc.CodeSize - 1;
+      Function->addObject(LowPC, HighPC);
+
+      // If the scope is a function, add it to the public names.
+      if ((options().getAttributePublics() || options().getPrintAnyLine()) &&
+          !Function->getIsInlinedFunction())
+        Reader->getCompileUnit()->addPublicName(Function, LowPC, HighPC);
+    }
+
+    if (Function->getIsSystem() && !options().getAttributeSystem()) {
+      Function->resetIncludeInPrint();
+      return Error::success();
+    }
+
+    TypeIndex TIFunctionType = Proc.FunctionType;
+    if (TIFunctionType.isSimple())
+      Function->setType(LogicalVisitor->getElement(StreamTPI, TIFunctionType));
+    else {
+      // We have to detect the correct stream, using the lexical parent
+      // name, as there is not other obvious way to get the stream.
+      //   Normal function: LF_FUNC_ID (TPI)/(IPI)
+      //                    LF_PROCEDURE (TPI)
+      //   Lambda function: LF_MFUNCTION (TPI)
+      //   Member function: LF_MFUNC_ID (TPI)/(IPI)
+
+      StringRef OuterComponent;
+      std::tie(OuterComponent, std::ignore) = getInnerComponent(Proc.Name);
+      TypeIndex TI = Shared->ForwardReferences.find(OuterComponent);
+
+      std::optional<CVType> CVFunctionType;
+      auto GetRecordType = [&]() -> bool {
+        CVFunctionType = Ids.tryGetType(TIFunctionType);
+        if (!CVFunctionType)
+          return false;
+
+        if (TI.isNoneType())
+          // Normal function.
+          if (CVFunctionType->kind() == LF_FUNC_ID)
+            return true;
+
+        // Member function.
+        return (CVFunctionType->kind() == LF_MFUNC_ID);
+      };
+
+      // We can have a LF_FUNC_ID, LF_PROCEDURE or LF_MFUNCTION.
+      if (!GetRecordType()) {
+        CVFunctionType = Types.tryGetType(TIFunctionType);
+        if (!CVFunctionType)
+          return llvm::make_error<CodeViewError>("Invalid type index");
+      }
+
+      if (Error Err = LogicalVisitor->finishVisitation(
+              *CVFunctionType, TIFunctionType, Function))
+        return Err;
+    }
+
+    if (Record.kind() == SymbolKind::S_GPROC32 ||
+        Record.kind() == SymbolKind::S_GPROC32_ID)
+      Function->setIsExternal();
+
+    // We don't have a way to see if the symbol is compiler generated. Use
+    // the linkage name, to detect `scalar deleting destructor' functions.
+    std::string DemangledSymbol = demangle(std::string(LinkageName));
+    if (DemangledSymbol.find("scalar deleting dtor") != std::string::npos) {
+      Function->setIsArtificial();
+    } else {
+      // Clang generates global ctor and dtor names containing the substrings:
+      // 'dynamic initializer for' and 'dynamic atexit destructor for'.
+      if (DemangledSymbol.find("dynamic atexit destructor for") !=
+          std::string::npos)
+        Function->setIsArtificial();
+    }
+  }
+
+  return Error::success();
+}
+
+// S_END
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        ScopeEndSym &ScopeEnd) {
+  InFunctionScope = false;
+  return Error::success();
+}
+
+// S_THUNK32
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) {
+  if (InFunctionScope)
+    return llvm::make_error<CodeViewError>("Visiting a Thunk32Sym while inside "
+                                           "function scope!");
+
+  InFunctionScope = true;
+
+  LLVM_DEBUG({
+    W.printHex("Segment", Thunk.Segment);
+    W.printString("Name", Thunk.Name);
+  });
+
+  if (LVScope *Function = LogicalVisitor->CurrentScope)
+    Function->setName(Thunk.Name);
+
+  return Error::success();
+}
+
+// S_UDT, S_COBOLUDT
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, UDTSym &UDT) {
+  LLVM_DEBUG({
+    printTypeIndex("Type", UDT.Type);
+    W.printString("UDTName", UDT.Name);
+  });
+
+  if (LVType *Type = LogicalVisitor->CurrentType) {
+    if (LVScope *Namespace = Shared->NamespaceDeduction.get(UDT.Name)) {
+      if (Type->getParentScope()->removeElement(Type))
+        Namespace->addElement(Type);
+    }
+
+    Type->setName(UDT.Name);
+
+    // We have to determine if the typedef is a real C/C++ definition or is
+    // the S_UDT record that describe all the user defined types.
+    //      0 | S_UDT `Name` original type = 0x1009
+    // 0x1009 | LF_STRUCTURE `Name`
+    // Ignore type definitions for RTTI types:
+    // _s__RTTIBaseClassArray, _s__RTTIBaseClassDescriptor,
+    // _s__RTTICompleteObjectLocator, _s__RTTIClassHierarchyDescriptor.
+    if (getReader().isSystemEntry(Type))
+      Type->resetIncludeInPrint();
+    else {
+      StringRef RecordName = getRecordName(Types, UDT.Type);
+      if (UDT.Name.equals(RecordName))
+        Type->resetIncludeInPrint();
+      Type->setType(LogicalVisitor->getElement(StreamTPI, UDT.Type));
+    }
+  }
+
+  return Error::success();
+}
+
+// S_UNAMESPACE
+Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record,
+                                        UsingNamespaceSym &UN) {
+  LLVM_DEBUG({ W.printString("Namespace", UN.Name); });
+  return Error::success();
+}
+
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "CodeViewLogicalVisitor"
+
+//===----------------------------------------------------------------------===//
+// Logical visitor.
+//===----------------------------------------------------------------------===//
+LVLogicalVisitor::LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W,
+                                   InputFile &Input)
+    : Reader(Reader), W(W), Input(Input) {
+  // The LogicalVisitor connects the CodeViewReader with the visitors that
+  // traverse the types, symbols, etc. Do any initialization that is needed.
+  Shared = std::make_shared<LVShared>(Reader, this);
+}
+
+void LVLogicalVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI,
+                                      uint32_t StreamIdx) {
+  codeview::printTypeIndex(W, FieldName, TI,
+                           StreamIdx == StreamTPI ? types() : ids());
+}
+
+void LVLogicalVisitor::printTypeBegin(CVType &Record, TypeIndex TI,
+                                      LVElement *Element, uint32_t StreamIdx) {
+  W.getOStream() << "\n";
+  W.startLine() << formatTypeLeafKind(Record.kind());
+  W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")";
+  W.getOStream() << " {\n";
+  W.indent();
+  W.printEnum("TypeLeafKind", unsigned(Record.kind()), ArrayRef(LeafTypeNames));
+  printTypeIndex("TI", TI, StreamIdx);
+  W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " "
+                << Element->getName() << "\n";
+}
+
+void LVLogicalVisitor::printTypeEnd(CVType &Record) {
+  W.unindent();
+  W.startLine() << "}\n";
+}
+
+void LVLogicalVisitor::printMemberBegin(CVMemberRecord &Record, TypeIndex TI,
+                                        LVElement *Element,
+                                        uint32_t StreamIdx) {
+  W.getOStream() << "\n";
+  W.startLine() << formatTypeLeafKind(Record.Kind);
+  W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")";
+  W.getOStream() << " {\n";
+  W.indent();
+  W.printEnum("TypeLeafKind", unsigned(Record.Kind), ArrayRef(LeafTypeNames));
+  printTypeIndex("TI", TI, StreamIdx);
+  W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " "
+                << Element->getName() << "\n";
+}
+
+void LVLogicalVisitor::printMemberEnd(CVMemberRecord &Record) {
+  W.unindent();
+  W.startLine() << "}\n";
+}
+
+Error LVLogicalVisitor::visitUnknownType(CVType &Record, TypeIndex TI) {
+  LLVM_DEBUG({
+    printTypeIndex("\nTI", TI, StreamTPI);
+    W.printNumber("Length", uint32_t(Record.content().size()));
+  });
+  return Error::success();
+}
+
+// LF_ARGLIST (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArgListRecord &Args,
+                                         TypeIndex TI, LVElement *Element) {
+  ArrayRef<TypeIndex> Indices = Args.getIndices();
+  uint32_t Size = Indices.size();
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printNumber("NumArgs", Size);
+    ListScope Arguments(W, "Arguments");
+    for (uint32_t I = 0; I < Size; ++I)
+      printTypeIndex("ArgType", Indices[I], StreamTPI);
+    printTypeEnd(Record);
+  });
+
+  LVScope *Function = static_cast<LVScope *>(Element);
+  for (uint32_t Index = 0; Index < Size; ++Index) {
+    TypeIndex ParameterType = Indices[Index];
+    createParameter(ParameterType, StringRef(), Function);
+  }
+
+  return Error::success();
+}
+
+// LF_ARRAY (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArrayRecord &AT,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("ElementType", AT.getElementType(), StreamTPI);
+    printTypeIndex("IndexType", AT.getIndexType(), StreamTPI);
+    W.printNumber("SizeOf", AT.getSize());
+    W.printString("Name", AT.getName());
+    printTypeEnd(Record);
+  });
+
+  if (Element->getIsFinalized())
+    return Error::success();
+  Element->setIsFinalized();
+
+  LVScopeArray *Array = static_cast<LVScopeArray *>(Element);
+  if (!Array)
+    return Error::success();
+
+  Reader->getCompileUnit()->addElement(Array);
+  TypeIndex TIElementType = AT.getElementType();
+
+  LVType *PrevSubrange = nullptr;
+  LazyRandomTypeCollection &Types = types();
+
+  // As the logical view is modeled on DWARF, for each dimension we have to
+  // create a DW_TAG_subrange_type, with dimension size.
+  // The subrange type can be: unsigned __int32 or unsigned __int64.
+  auto AddSubrangeType = [&](ArrayRecord &AR) {
+    LVType *Subrange = Reader->createTypeSubrange();
+    Subrange->setTag(dwarf::DW_TAG_subrange_type);
+    Subrange->setType(getElement(StreamTPI, AR.getIndexType()));
+    Subrange->setCount(AR.getSize());
+    Subrange->setOffset(
+        TIElementType.isSimple()
+            ? (uint32_t)(TypeLeafKind)TIElementType.getSimpleKind()
+            : TIElementType.getIndex());
+    Array->addElement(Subrange);
+
+    if (PrevSubrange)
+      if (int64_t Count = Subrange->getCount())
+        PrevSubrange->setCount(PrevSubrange->getCount() / Count);
+    PrevSubrange = Subrange;
+  };
+
+  // Preserve the original TypeIndex; it would be updated in the case of:
+  // - The array type contains qualifiers.
+  // - In multidimensional arrays, the last LF_ARRAY entry contains the type.
+  TypeIndex TIArrayType;
+
+  // For each dimension in the array, there is a LF_ARRAY entry. The last
+  // entry contains the array type, which can be a LF_MODIFIER in the case
+  // of the type being modified by a qualifier (const, etc).
+  ArrayRecord AR(AT);
+  CVType CVEntry = Record;
+  while (CVEntry.kind() == LF_ARRAY) {
+    // Create the subrange information, required by the logical view. Once
+    // the array has been processed, the dimension sizes will updated, as
+    // the sizes are a progression. For instance:
+    // sizeof(int) = 4
+    // int Array[2];        Sizes:  8          Dim: 8  /  4 -> [2]
+    // int Array[2][3];     Sizes: 24, 12      Dim: 24 / 12 -> [2]
+    //                                         Dim: 12 /  4 ->    [3]
+    // int Array[2][3][4];  sizes: 96, 48, 16  Dim: 96 / 48 -> [2]
+    //                                         Dim: 48 / 16 ->    [3]
+    //                                         Dim: 16 /  4 ->       [4]
+    AddSubrangeType(AR);
+    TIArrayType = TIElementType;
+
+    // The current ElementType can be a modifier, in which case we need to
+    // get the type being modified.
+    // If TypeIndex is not a simple type, check if we have a qualified type.
+    if (!TIElementType.isSimple()) {
+      CVType CVElementType = Types.getType(TIElementType);
+      if (CVElementType.kind() == LF_MODIFIER) {
+        LVElement *QualifiedType =
+            Shared->TypeRecords.find(StreamTPI, TIElementType);
+        if (Error Err =
+                finishVisitation(CVElementType, TIElementType, QualifiedType))
+          return Err;
+        // Get the TypeIndex of the type that the LF_MODIFIER modifies.
+        TIElementType = getModifiedType(CVElementType);
+      }
+    }
+    // Ends the traversal, as we have reached a simple type (int, char, etc).
+    if (TIElementType.isSimple())
+      break;
+
+    // Read next dimension linked entry, if any.
+    CVEntry = Types.getType(TIElementType);
+    if (Error Err = TypeDeserializer::deserializeAs(
+            const_cast<CVType &>(CVEntry), AR)) {
+      consumeError(std::move(Err));
+      break;
+    }
+    TIElementType = AR.getElementType();
+    // NOTE: The typeindex has a value of: 0x0280.0000
+    getTrueType(TIElementType);
+  }
+
+  Array->setName(AT.getName());
+  TIArrayType = Shared->ForwardReferences.remap(TIArrayType);
+  Array->setType(getElement(StreamTPI, TIArrayType));
+
+  if (PrevSubrange)
+    // In the case of an aggregate type (class, struct, union, interface),
+    // get the aggregate size. As the original record is pointing to its
+    // reference, we have to update it.
+    if (uint64_t Size =
+            isAggregate(CVEntry)
+                ? getSizeInBytesForTypeRecord(Types.getType(TIArrayType))
+                : getSizeInBytesForTypeIndex(TIElementType))
+      PrevSubrange->setCount(PrevSubrange->getCount() / Size);
+
+  return Error::success();
+}
+
+// LF_BITFIELD (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BitFieldRecord &BF,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("Type", TI, StreamTPI);
+    W.printNumber("BitSize", BF.getBitSize());
+    W.printNumber("BitOffset", BF.getBitOffset());
+    printTypeEnd(Record);
+  });
+
+  Element->setType(getElement(StreamTPI, BF.getType()));
+  Element->setBitSize(BF.getBitSize());
+  return Error::success();
+}
+
+// LF_BUILDINFO (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &BI,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamIPI);
+    W.printNumber("NumArgs", static_cast<uint32_t>(BI.getArgs().size()));
+    ListScope Arguments(W, "Arguments");
+    for (TypeIndex Arg : BI.getArgs())
+      printTypeIndex("ArgType", Arg, StreamIPI);
+    printTypeEnd(Record);
+  });
+
+  // The given 'Element' refers to the current compilation unit.
+  // All the args are references into the TPI/IPI stream.
+  TypeIndex TIName = BI.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile];
+  std::string Name = std::string(ids().getTypeName(TIName));
+
+  // There are cases where LF_BUILDINFO fields are empty.
+  if (!Name.empty())
+    Element->setName(Name);
+
+  return Error::success();
+}
+
+// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printNumber("MemberCount", Class.getMemberCount());
+    printTypeIndex("FieldList", Class.getFieldList(), StreamTPI);
+    printTypeIndex("DerivedFrom", Class.getDerivationList(), StreamTPI);
+    printTypeIndex("VShape", Class.getVTableShape(), StreamTPI);
+    W.printNumber("SizeOf", Class.getSize());
+    W.printString("Name", Class.getName());
+    if (Class.hasUniqueName())
+      W.printString("UniqueName", Class.getUniqueName());
+    printTypeEnd(Record);
+  });
+
+  if (Element->getIsFinalized())
+    return Error::success();
+  Element->setIsFinalized();
+
+  LVScopeAggregate *Scope = static_cast<LVScopeAggregate *>(Element);
+  if (!Scope)
+    return Error::success();
+
+  Scope->setName(Class.getName());
+  if (Class.hasUniqueName())
+    Scope->setLinkageName(Class.getUniqueName());
+
+  if (Class.isNested()) {
+    Scope->setIsNested();
+    createParents(Class.getName(), Scope);
+  }
+
+  if (Class.isScoped())
+    Scope->setIsScoped();
+
+  // Nested types will be added to their parents at creation. The forward
+  // references are only processed to finish the referenced element creation.
+  if (!(Class.isNested() || Class.isScoped())) {
+    if (LVScope *Namespace = Shared->NamespaceDeduction.get(Class.getName()))
+      Namespace->addElement(Scope);
+    else
+      Reader->getCompileUnit()->addElement(Scope);
+  }
+
+  LazyRandomTypeCollection &Types = types();
+  TypeIndex TIFieldList = Class.getFieldList();
+  if (TIFieldList.isNoneType()) {
+    TypeIndex ForwardType = Shared->ForwardReferences.find(Class.getName());
+    if (!ForwardType.isNoneType()) {
+      CVType CVReference = Types.getType(ForwardType);
+      TypeRecordKind RK = static_cast<TypeRecordKind>(CVReference.kind());
+      ClassRecord ReferenceRecord(RK);
+      if (Error Err = TypeDeserializer::deserializeAs(
+              const_cast<CVType &>(CVReference), ReferenceRecord))
+        return Err;
+      TIFieldList = ReferenceRecord.getFieldList();
+    }
+  }
+
+  if (!TIFieldList.isNoneType()) {
+    // Pass down the TypeIndex 'TI' for the aggregate containing the field list.
+    CVType CVFieldList = Types.getType(TIFieldList);
+    if (Error Err = finishVisitation(CVFieldList, TI, Scope))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+// LF_ENUM (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printNumber("NumEnumerators", Enum.getMemberCount());
+    printTypeIndex("UnderlyingType", Enum.getUnderlyingType(), StreamTPI);
+    printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI);
+    W.printString("Name", Enum.getName());
+    printTypeEnd(Record);
+  });
+
+  LVScopeEnumeration *Scope = static_cast<LVScopeEnumeration *>(Element);
+  if (!Scope)
+    return Error::success();
+
+  if (Scope->getIsFinalized())
+    return Error::success();
+  Scope->setIsFinalized();
+
+  // Set the name, as in the case of nested, it would determine the relation
+  // to any potential parent, via the LF_NESTTYPE record.
+  Scope->setName(Enum.getName());
+  if (Enum.hasUniqueName())
+    Scope->setLinkageName(Enum.getUniqueName());
+
+  Scope->setType(getElement(StreamTPI, Enum.getUnderlyingType()));
+
+  if (Enum.isNested()) {
+    Scope->setIsNested();
+    createParents(Enum.getName(), Scope);
+  }
+
+  if (Enum.isScoped()) {
+    Scope->setIsScoped();
+    Scope->setIsEnumClass();
+  }
+
+  // Nested types will be added to their parents at creation.
+  if (!(Enum.isNested() || Enum.isScoped())) {
+    if (LVScope *Namespace = Shared->NamespaceDeduction.get(Enum.getName()))
+      Namespace->addElement(Scope);
+    else
+      Reader->getCompileUnit()->addElement(Scope);
+  }
+
+  TypeIndex TIFieldList = Enum.getFieldList();
+  if (!TIFieldList.isNoneType()) {
+    LazyRandomTypeCollection &Types = types();
+    CVType CVFieldList = Types.getType(TIFieldList);
+    if (Error Err = finishVisitation(CVFieldList, TIFieldList, Scope))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+// LF_FIELDLIST (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         FieldListRecord &FieldList,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeEnd(Record);
+  });
+
+  if (Error Err = visitFieldListMemberStream(TI, Element, FieldList.Data))
+    return Err;
+
+  return Error::success();
+}
+
+// LF_FUNC_ID (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func,
+                                         TypeIndex TI, LVElement *Element) {
+  // ParentScope and FunctionType are references into the TPI stream.
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamIPI);
+    printTypeIndex("ParentScope", Func.getParentScope(), StreamTPI);
+    printTypeIndex("FunctionType", Func.getFunctionType(), StreamTPI);
+    W.printString("Name", Func.getName());
+    printTypeEnd(Record);
+  });
+
+  // The TypeIndex (LF_PROCEDURE) returned by 'getFunctionType' is the
+  // function propotype, we need to use the function definition.
+  if (LVScope *FunctionDcl = static_cast<LVScope *>(Element)) {
+    // For inlined functions, the inlined instance has been already processed
+    // (all its information is contained in the Symbols section).
+    // 'Element' points to the created 'abstract' (out-of-line) function.
+    // Use the parent scope information to allocate it to the correct scope.
+    LazyRandomTypeCollection &Types = types();
+    TypeIndex TIParent = Func.getParentScope();
+    if (FunctionDcl->getIsInlinedAbstract()) {
+      FunctionDcl->setName(Func.getName());
+      if (TIParent.isNoneType())
+        Reader->getCompileUnit()->addElement(FunctionDcl);
+    }
+
+    if (!TIParent.isNoneType()) {
+      CVType CVParentScope = ids().getType(TIParent);
+      if (Error Err = finishVisitation(CVParentScope, TIParent, FunctionDcl))
+        return Err;
+    }
+
+    TypeIndex TIFunctionType = Func.getFunctionType();
+    CVType CVFunctionType = Types.getType(TIFunctionType);
+    if (Error Err =
+            finishVisitation(CVFunctionType, TIFunctionType, FunctionDcl))
+      return Err;
+
+    FunctionDcl->setIsFinalized();
+  }
+
+  return Error::success();
+}
+
+// LF_LABEL (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, LabelRecord &LR,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_MFUNC_ID (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id,
+                                         TypeIndex TI, LVElement *Element) {
+  // ClassType and FunctionType are references into the TPI stream.
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamIPI);
+    printTypeIndex("ClassType", Id.getClassType(), StreamTPI);
+    printTypeIndex("FunctionType", Id.getFunctionType(), StreamTPI);
+    W.printString("Name", Id.getName());
+    printTypeEnd(Record);
+  });
+
+  LVScope *FunctionDcl = static_cast<LVScope *>(Element);
+  if (FunctionDcl->getIsInlinedAbstract()) {
+    // For inlined functions, the inlined instance has been already processed
+    // (all its information is contained in the Symbols section).
+    // 'Element' points to the created 'abstract' (out-of-line) function.
+    // Use the parent scope information to allocate it to the correct scope.
+    if (LVScope *Class = static_cast<LVScope *>(
+            Shared->TypeRecords.find(StreamTPI, Id.getClassType())))
+      Class->addElement(FunctionDcl);
+  }
+
+  TypeIndex TIFunctionType = Id.getFunctionType();
+  CVType CVFunction = types().getType(TIFunctionType);
+  if (Error Err = finishVisitation(CVFunction, TIFunctionType, Element))
+    return Err;
+
+  return Error::success();
+}
+
+// LF_MFUNCTION (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         MemberFunctionRecord &MF, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("ReturnType", MF.getReturnType(), StreamTPI);
+    printTypeIndex("ClassType", MF.getClassType(), StreamTPI);
+    printTypeIndex("ThisType", MF.getThisType(), StreamTPI);
+    W.printNumber("NumParameters", MF.getParameterCount());
+    printTypeIndex("ArgListType", MF.getArgumentList(), StreamTPI);
+    W.printNumber("ThisAdjustment", MF.getThisPointerAdjustment());
+    printTypeEnd(Record);
+  });
+
+  if (LVScope *MemberFunction = static_cast<LVScope *>(Element)) {
+    LVElement *Class = getElement(StreamTPI, MF.getClassType());
+
+    MemberFunction->setIsFinalized();
+    MemberFunction->setType(getElement(StreamTPI, MF.getReturnType()));
+    MemberFunction->setOffset(TI.getIndex());
+    MemberFunction->setOffsetFromTypeIndex();
+
+    if (ProcessArgumentList) {
+      ProcessArgumentList = false;
+
+      if (!MemberFunction->getIsStatic()) {
+        LVElement *ThisPointer = getElement(StreamTPI, MF.getThisType());
+        // When creating the 'this' pointer, check if it points to a reference.
+        ThisPointer->setType(Class);
+        LVSymbol *This =
+            createParameter(ThisPointer, StringRef(), MemberFunction);
+        This->setIsArtificial();
+      }
+
+      // Create formal parameters.
+      LazyRandomTypeCollection &Types = types();
+      CVType CVArguments = Types.getType(MF.getArgumentList());
+      if (Error Err = finishVisitation(CVArguments, MF.getArgumentList(),
+                                       MemberFunction))
+        return Err;
+    }
+  }
+
+  return Error::success();
+}
+
+// LF_METHODLIST (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         MethodOverloadListRecord &Overloads,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeEnd(Record);
+  });
+
+  for (OneMethodRecord &Method : Overloads.Methods) {
+    CVMemberRecord Record;
+    Record.Kind = LF_METHOD;
+    Method.Name = OverloadedMethodName;
+    if (Error Err = visitKnownMember(Record, Method, TI, Element))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+// LF_MODIFIER (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ModifierRecord &Mod,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("ModifiedType", Mod.getModifiedType(), StreamTPI);
+    printTypeEnd(Record);
+  });
+
+  // Create the modified type, which will be attached to the type(s) that
+  // contains the modifiers.
+  LVElement *ModifiedType = getElement(StreamTPI, Mod.getModifiedType());
+
+  // At this point the types recording the qualifiers do not have a
+  // scope parent. They must be assigned to the current compile unit.
+  LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit();
+
+  // The incoming element does not have a defined kind. Use the given
+  // modifiers to complete its type. A type can have more than one modifier;
+  // in that case, we have to create an extra type to have the other modifier.
+  LVType *LastLink = static_cast<LVType *>(Element);
+  if (!LastLink->getParentScope())
+    CompileUnit->addElement(LastLink);
+
+  bool SeenModifier = false;
+  uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
+  if (Mods & uint16_t(ModifierOptions::Const)) {
+    SeenModifier = true;
+    LastLink->setTag(dwarf::DW_TAG_const_type);
+    LastLink->setIsConst();
+    LastLink->setName("const");
+  }
+  if (Mods & uint16_t(ModifierOptions::Volatile)) {
+    if (SeenModifier) {
+      LVType *Volatile = Reader->createType();
+      Volatile->setIsModifier();
+      LastLink->setType(Volatile);
+      LastLink = Volatile;
+      CompileUnit->addElement(LastLink);
+    }
+    LastLink->setTag(dwarf::DW_TAG_volatile_type);
+    LastLink->setIsVolatile();
+    LastLink->setName("volatile");
+  }
+  if (Mods & uint16_t(ModifierOptions::Unaligned)) {
+    if (SeenModifier) {
+      LVType *Unaligned = Reader->createType();
+      Unaligned->setIsModifier();
+      LastLink->setType(Unaligned);
+      LastLink = Unaligned;
+      CompileUnit->addElement(LastLink);
+    }
+    LastLink->setTag(dwarf::DW_TAG_unaligned);
+    LastLink->setIsUnaligned();
+    LastLink->setName("unaligned");
+  }
+
+  LastLink->setType(ModifiedType);
+  return Error::success();
+}
+
+// LF_POINTER (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PointerRecord &Ptr,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("PointeeType", Ptr.getReferentType(), StreamTPI);
+    W.printNumber("IsFlat", Ptr.isFlat());
+    W.printNumber("IsConst", Ptr.isConst());
+    W.printNumber("IsVolatile", Ptr.isVolatile());
+    W.printNumber("IsUnaligned", Ptr.isUnaligned());
+    W.printNumber("IsRestrict", Ptr.isRestrict());
+    W.printNumber("IsThisPtr&", Ptr.isLValueReferenceThisPtr());
+    W.printNumber("IsThisPtr&&", Ptr.isRValueReferenceThisPtr());
+    W.printNumber("SizeOf", Ptr.getSize());
+
+    if (Ptr.isPointerToMember()) {
+      const MemberPointerInfo &MI = Ptr.getMemberInfo();
+      printTypeIndex("ClassType", MI.getContainingType(), StreamTPI);
+    }
+    printTypeEnd(Record);
+  });
+
+  // Find the pointed-to type.
+  LVType *Pointer = static_cast<LVType *>(Element);
+  LVElement *Pointee = nullptr;
+
+  PointerMode Mode = Ptr.getMode();
+  Pointee = Ptr.isPointerToMember()
+                ? Shared->TypeRecords.find(StreamTPI, Ptr.getReferentType())
+                : getElement(StreamTPI, Ptr.getReferentType());
+
+  // At this point the types recording the qualifiers do not have a
+  // scope parent. They must be assigned to the current compile unit.
+  LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit();
+
+  // Order for the 
diff erent modifiers:
+  // <restrict> <pointer, Reference, ValueReference> <const, volatile>
+  // Const and volatile already processed.
+  bool SeenModifier = false;
+  LVType *LastLink = Pointer;
+  if (!LastLink->getParentScope())
+    CompileUnit->addElement(LastLink);
+
+  if (Ptr.isRestrict()) {
+    SeenModifier = true;
+    LVType *Restrict = Reader->createType();
+    Restrict->setTag(dwarf::DW_TAG_restrict_type);
+    Restrict->setIsRestrict();
+    Restrict->setName("restrict");
+    LastLink->setType(Restrict);
+    LastLink = Restrict;
+    CompileUnit->addElement(LastLink);
+  }
+  if (Mode == PointerMode::LValueReference) {
+    if (SeenModifier) {
+      LVType *LReference = Reader->createType();
+      LReference->setIsModifier();
+      LastLink->setType(LReference);
+      LastLink = LReference;
+      CompileUnit->addElement(LastLink);
+    }
+    LastLink->setTag(dwarf::DW_TAG_reference_type);
+    LastLink->setIsReference();
+    LastLink->setName("&");
+  }
+  if (Mode == PointerMode::RValueReference) {
+    if (SeenModifier) {
+      LVType *RReference = Reader->createType();
+      RReference->setIsModifier();
+      LastLink->setType(RReference);
+      LastLink = RReference;
+      CompileUnit->addElement(LastLink);
+    }
+    LastLink->setTag(dwarf::DW_TAG_rvalue_reference_type);
+    LastLink->setIsRvalueReference();
+    LastLink->setName("&&");
+  }
+
+  // When creating the pointer, check if it points to a reference.
+  LastLink->setType(Pointee);
+  return Error::success();
+}
+
+// LF_PROCEDURE (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI);
+    W.printNumber("NumParameters", Proc.getParameterCount());
+    printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI);
+    printTypeEnd(Record);
+  });
+
+  // There is no need to traverse the argument list, as the CodeView format
+  // declares the parameters as a 'S_LOCAL' symbol tagged as parameter.
+  // Only process parameters when dealing with inline functions.
+  if (LVScope *FunctionDcl = static_cast<LVScope *>(Element)) {
+    FunctionDcl->setType(getElement(StreamTPI, Proc.getReturnType()));
+
+    if (ProcessArgumentList) {
+      ProcessArgumentList = false;
+      // Create formal parameters.
+      LazyRandomTypeCollection &Types = types();
+      CVType CVArguments = Types.getType(Proc.getArgumentList());
+      if (Error Err = finishVisitation(CVArguments, Proc.getArgumentList(),
+                                       FunctionDcl))
+        return Err;
+    }
+  }
+
+  return Error::success();
+}
+
+// LF_UNION (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printNumber("MemberCount", Union.getMemberCount());
+    printTypeIndex("FieldList", Union.getFieldList(), StreamTPI);
+    W.printNumber("SizeOf", Union.getSize());
+    W.printString("Name", Union.getName());
+    if (Union.hasUniqueName())
+      W.printString("UniqueName", Union.getUniqueName());
+    printTypeEnd(Record);
+  });
+
+  LVScopeAggregate *Scope = static_cast<LVScopeAggregate *>(Element);
+  if (!Scope)
+    return Error::success();
+
+  if (Scope->getIsFinalized())
+    return Error::success();
+  Scope->setIsFinalized();
+
+  Scope->setName(Union.getName());
+  if (Union.hasUniqueName())
+    Scope->setLinkageName(Union.getUniqueName());
+
+  if (Union.isNested()) {
+    Scope->setIsNested();
+    createParents(Union.getName(), Scope);
+  } else {
+    if (LVScope *Namespace = Shared->NamespaceDeduction.get(Union.getName()))
+      Namespace->addElement(Scope);
+    else
+      Reader->getCompileUnit()->addElement(Scope);
+  }
+
+  if (!Union.getFieldList().isNoneType()) {
+    LazyRandomTypeCollection &Types = types();
+    // Pass down the TypeIndex 'TI' for the aggregate containing the field list.
+    CVType CVFieldList = Types.getType(Union.getFieldList());
+    if (Error Err = finishVisitation(CVFieldList, TI, Scope))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+// LF_TYPESERVER2 (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, TypeServer2Record &TS,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printString("Guid", formatv("{0}", TS.getGuid()).str());
+    W.printNumber("Age", TS.getAge());
+    W.printString("Name", TS.getName());
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_VFTABLE (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, VFTableRecord &VFT,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("CompleteClass", VFT.getCompleteClass(), StreamTPI);
+    printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable(), StreamTPI);
+    W.printHex("VFPtrOffset", VFT.getVFPtrOffset());
+    W.printString("VFTableName", VFT.getName());
+    for (const StringRef &N : VFT.getMethodNames())
+      W.printString("MethodName", N);
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_VTSHAPE (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         VFTableShapeRecord &Shape,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printNumber("VFEntryCount", Shape.getEntryCount());
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_SUBSTR_LIST (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         StringListRecord &Strings,
+                                         TypeIndex TI, LVElement *Element) {
+  // All the indices are references into the TPI/IPI stream.
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamIPI);
+    ArrayRef<TypeIndex> Indices = Strings.getIndices();
+    uint32_t Size = Indices.size();
+    W.printNumber("NumStrings", Size);
+    ListScope Arguments(W, "Strings");
+    for (uint32_t I = 0; I < Size; ++I)
+      printTypeIndex("String", Indices[I], StreamIPI);
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_STRING_ID (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String,
+                                         TypeIndex TI, LVElement *Element) {
+  // All args are references into the TPI/IPI stream.
+  LLVM_DEBUG({
+    printTypeIndex("\nTI", TI, StreamIPI);
+    printTypeIndex("Id", String.getId(), StreamIPI);
+    W.printString("StringData", String.getString());
+  });
+
+  if (LVScope *Namespace = Shared->NamespaceDeduction.get(
+          String.getString(), /*CheckScope=*/false)) {
+    // The function is already at 
diff erent scope. In order to reflect
+    // the correct parent, move it to the namespace.
+    if (LVScope *Scope = Element->getParentScope())
+      Scope->removeElement(Element);
+    Namespace->addElement(Element);
+  }
+
+  return Error::success();
+}
+
+// LF_UDT_SRC_LINE (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         UdtSourceLineRecord &SourceLine,
+                                         TypeIndex TI, LVElement *Element) {
+  // All args are references into the TPI/IPI stream.
+  LLVM_DEBUG({
+    printTypeIndex("\nTI", TI, StreamIPI);
+    printTypeIndex("UDT", SourceLine.getUDT(), StreamIPI);
+    printTypeIndex("SourceFile", SourceLine.getSourceFile(), StreamIPI);
+    W.printNumber("LineNumber", SourceLine.getLineNumber());
+  });
+  return Error::success();
+}
+
+// LF_UDT_MOD_SRC_LINE (TPI)/(IPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         UdtModSourceLineRecord &ModSourceLine,
+                                         TypeIndex TI, LVElement *Element) {
+  // All args are references into the TPI/IPI stream.
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamIPI);
+    printTypeIndex("\nTI", TI, StreamIPI);
+    printTypeIndex("UDT", ModSourceLine.getUDT(), StreamIPI);
+    printTypeIndex("SourceFile", ModSourceLine.getSourceFile(), StreamIPI);
+    W.printNumber("LineNumber", ModSourceLine.getLineNumber());
+    W.printNumber("Module", ModSourceLine.getModule());
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_PRECOMP (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PrecompRecord &Precomp,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printHex("StartIndex", Precomp.getStartTypeIndex());
+    W.printHex("Count", Precomp.getTypesCount());
+    W.printHex("Signature", Precomp.getSignature());
+    W.printString("PrecompFile", Precomp.getPrecompFilePath());
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_ENDPRECOMP (TPI)
+Error LVLogicalVisitor::visitKnownRecord(CVType &Record,
+                                         EndPrecompRecord &EndPrecomp,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printTypeBegin(Record, TI, Element, StreamTPI);
+    W.printHex("Signature", EndPrecomp.getSignature());
+    printTypeEnd(Record);
+  });
+  return Error::success();
+}
+
+Error LVLogicalVisitor::visitUnknownMember(CVMemberRecord &Record,
+                                           TypeIndex TI) {
+  LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); });
+  return Error::success();
+}
+
+// LF_BCLASS, LF_BINTERFACE
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         BaseClassRecord &Base, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("BaseType", Base.getBaseType(), StreamTPI);
+    W.printHex("BaseOffset", Base.getBaseOffset());
+    printMemberEnd(Record);
+  });
+
+  createElement(Record.Kind);
+  if (LVSymbol *Symbol = CurrentSymbol) {
+    LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType());
+    Symbol->setName(BaseClass->getName());
+    Symbol->setType(BaseClass);
+    Symbol->setAccessibilityCode(Base.getAccess());
+    static_cast<LVScope *>(Element)->addElement(Symbol);
+  }
+
+  return Error::success();
+}
+
+// LF_MEMBER
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         DataMemberRecord &Field, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("Type", Field.getType(), StreamTPI);
+    W.printHex("FieldOffset", Field.getFieldOffset());
+    W.printString("Name", Field.getName());
+    printMemberEnd(Record);
+  });
+
+  // Create the data member.
+  createDataMember(Record, static_cast<LVScope *>(Element), Field.getName(),
+                   Field.getType(), Field.getAccess());
+  return Error::success();
+}
+
+// LF_ENUMERATE
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         EnumeratorRecord &Enum, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    W.printNumber("EnumValue", Enum.getValue());
+    W.printString("Name", Enum.getName());
+    printMemberEnd(Record);
+  });
+
+  createElement(Record.Kind);
+  if (LVType *Type = CurrentType) {
+    Type->setName(Enum.getName());
+    SmallString<16> Value;
+    Enum.getValue().toString(Value, 16, true, true);
+    Type->setValue(Value);
+    static_cast<LVScope *>(Element)->addElement(CurrentType);
+  }
+
+  return Error::success();
+}
+
+// LF_INDEX
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         ListContinuationRecord &Cont,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("ContinuationIndex", Cont.getContinuationIndex(), StreamTPI);
+    printMemberEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_NESTTYPE
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         NestedTypeRecord &Nested, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("Type", Nested.getNestedType(), StreamTPI);
+    W.printString("Name", Nested.getName());
+    printMemberEnd(Record);
+  });
+
+  if (LVElement *Typedef = createElement(SymbolKind::S_UDT)) {
+    Typedef->setName(Nested.getName());
+    LVElement *NestedType = getElement(StreamTPI, Nested.getNestedType());
+    Typedef->setType(NestedType);
+    LVScope *Scope = static_cast<LVScope *>(Element);
+    Scope->addElement(Typedef);
+
+    if (NestedType && NestedType->getIsNested()) {
+      // 'Element' is an aggregate type that may contains this nested type
+      // definition. Used their scoped names, to decide on their relationship.
+      StringRef RecordName = getRecordName(types(), TI);
+
+      StringRef NestedTypeName = NestedType->getName();
+      if (NestedTypeName.size() && RecordName.size()) {
+        StringRef OuterComponent;
+        std::tie(OuterComponent, std::ignore) =
+            getInnerComponent(NestedTypeName);
+        // We have an already created nested type. Add it to the current scope
+        // and update all its children if any.
+        if (OuterComponent.size() && OuterComponent.equals(RecordName)) {
+          if (!NestedType->getIsScopedAlready()) {
+            Scope->addElement(NestedType);
+            NestedType->setIsScopedAlready();
+            NestedType->updateLevel(Scope);
+          }
+          Typedef->resetIncludeInPrint();
+        }
+      }
+    }
+  }
+
+  return Error::success();
+}
+
+// LF_ONEMETHOD
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         OneMethodRecord &Method, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("Type", Method.getType(), StreamTPI);
+    // If virtual, then read the vftable offset.
+    if (Method.isIntroducingVirtual())
+      W.printHex("VFTableOffset", Method.getVFTableOffset());
+    W.printString("Name", Method.getName());
+    printMemberEnd(Record);
+  });
+
+  // All the LF_ONEMETHOD objects share the same type description.
+  // We have to create a scope object for each one and get the required
+  // information from the LF_MFUNCTION object.
+  ProcessArgumentList = true;
+  if (LVElement *MemberFunction = createElement(TypeLeafKind::LF_ONEMETHOD)) {
+    MemberFunction->setIsFinalized();
+    static_cast<LVScope *>(Element)->addElement(MemberFunction);
+
+    MemberFunction->setName(Method.getName());
+    MemberFunction->setAccessibilityCode(Method.getAccess());
+
+    MethodKind Kind = Method.getMethodKind();
+    if (Kind == MethodKind::Static)
+      MemberFunction->setIsStatic();
+    MemberFunction->setVirtualityCode(Kind);
+
+    MethodOptions Flags = Method.Attrs.getFlags();
+    if (MethodOptions::CompilerGenerated ==
+        (Flags & MethodOptions::CompilerGenerated))
+      MemberFunction->setIsArtificial();
+
+    LazyRandomTypeCollection &Types = types();
+    CVType CVMethodType = Types.getType(Method.getType());
+    if (Error Err =
+            finishVisitation(CVMethodType, Method.getType(), MemberFunction))
+      return Err;
+  }
+  ProcessArgumentList = false;
+
+  return Error::success();
+}
+
+// LF_METHOD
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         OverloadedMethodRecord &Method,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    W.printHex("MethodCount", Method.getNumOverloads());
+    printTypeIndex("MethodListIndex", Method.getMethodList(), StreamTPI);
+    W.printString("Name", Method.getName());
+    printMemberEnd(Record);
+  });
+
+  // Record the overloaded method name, which will be used during the
+  // traversal of the method list.
+  LazyRandomTypeCollection &Types = types();
+  OverloadedMethodName = Method.getName();
+  CVType CVMethods = Types.getType(Method.getMethodList());
+  if (Error Err = finishVisitation(CVMethods, Method.getMethodList(), Element))
+    return Err;
+
+  return Error::success();
+}
+
+// LF_STMEMBER
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         StaticDataMemberRecord &Field,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("Type", Field.getType(), StreamTPI);
+    W.printString("Name", Field.getName());
+    printMemberEnd(Record);
+  });
+
+  // Create the data member.
+  createDataMember(Record, static_cast<LVScope *>(Element), Field.getName(),
+                   Field.getType(), Field.getAccess());
+  return Error::success();
+}
+
+// LF_VFUNCTAB
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         VFPtrRecord &VFTable, TypeIndex TI,
+                                         LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("Type", VFTable.getType(), StreamTPI);
+    printMemberEnd(Record);
+  });
+  return Error::success();
+}
+
+// LF_VBCLASS, LF_IVBCLASS
+Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record,
+                                         VirtualBaseClassRecord &Base,
+                                         TypeIndex TI, LVElement *Element) {
+  LLVM_DEBUG({
+    printMemberBegin(Record, TI, Element, StreamTPI);
+    printTypeIndex("BaseType", Base.getBaseType(), StreamTPI);
+    printTypeIndex("VBPtrType", Base.getVBPtrType(), StreamTPI);
+    W.printHex("VBPtrOffset", Base.getVBPtrOffset());
+    W.printHex("VBTableIndex", Base.getVTableIndex());
+    printMemberEnd(Record);
+  });
+
+  createElement(Record.Kind);
+  if (LVSymbol *Symbol = CurrentSymbol) {
+    LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType());
+    Symbol->setName(BaseClass->getName());
+    Symbol->setType(BaseClass);
+    Symbol->setAccessibilityCode(Base.getAccess());
+    Symbol->setVirtualityCode(MethodKind::Virtual);
+    static_cast<LVScope *>(Element)->addElement(Symbol);
+  }
+
+  return Error::success();
+}
+
+Error LVLogicalVisitor::visitMemberRecord(CVMemberRecord &Record,
+                                          TypeVisitorCallbacks &Callbacks,
+                                          TypeIndex TI, LVElement *Element) {
+  if (Error Err = Callbacks.visitMemberBegin(Record))
+    return Err;
+
+  switch (Record.Kind) {
+  default:
+    if (Error Err = Callbacks.visitUnknownMember(Record))
+      return Err;
+    break;
+#define MEMBER_RECORD(EnumName, EnumVal, Name)                                 \
+  case EnumName: {                                                             \
+    if (Error Err =                                                            \
+            visitKnownMember<Name##Record>(Record, Callbacks, TI, Element))    \
+      return Err;                                                              \
+    break;                                                                     \
+  }
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)                \
+  MEMBER_RECORD(EnumVal, EnumVal, AliasName)
+#define TYPE_RECORD(EnumName, EnumVal, Name)
+#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+  }
+
+  if (Error Err = Callbacks.visitMemberEnd(Record))
+    return Err;
+
+  return Error::success();
+}
+
+Error LVLogicalVisitor::finishVisitation(CVType &Record, TypeIndex TI,
+                                         LVElement *Element) {
+  switch (Record.kind()) {
+  default:
+    if (Error Err = visitUnknownType(Record, TI))
+      return Err;
+    break;
+#define TYPE_RECORD(EnumName, EnumVal, Name)                                   \
+  case EnumName: {                                                             \
+    if (Error Err = visitKnownRecord<Name##Record>(Record, TI, Element))       \
+      return Err;                                                              \
+    break;                                                                     \
+  }
+#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)                  \
+  TYPE_RECORD(EnumVal, EnumVal, AliasName)
+#define MEMBER_RECORD(EnumName, EnumVal, Name)
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+  }
+
+  return Error::success();
+}
+
+// Customized version of 'FieldListVisitHelper'.
+Error LVLogicalVisitor::visitFieldListMemberStream(
+    TypeIndex TI, LVElement *Element, ArrayRef<uint8_t> FieldList) {
+  BinaryByteStream Stream(FieldList, llvm::support::little);
+  BinaryStreamReader Reader(Stream);
+  FieldListDeserializer Deserializer(Reader);
+  TypeVisitorCallbackPipeline Pipeline;
+  Pipeline.addCallbackToPipeline(Deserializer);
+
+  TypeLeafKind Leaf;
+  while (!Reader.empty()) {
+    if (Error Err = Reader.readEnum(Leaf))
+      return Err;
+
+    CVMemberRecord Record;
+    Record.Kind = Leaf;
+    if (Error Err = visitMemberRecord(Record, Pipeline, TI, Element))
+      return Err;
+  }
+
+  return Error::success();
+}
+
+void LVLogicalVisitor::addElement(LVScope *Scope, bool IsCompileUnit) {
+  // The CodeView specifications does not treat S_COMPILE2 and S_COMPILE3
+  // as symbols that open a scope. The CodeView reader, treat them in a
+  // similar way as DWARF. As there is no a symbole S_END to close the
+  // compile unit, we need to check for the next compile unit.
+  if (IsCompileUnit) {
+    if (!ScopeStack.empty())
+      popScope();
+    InCompileUnitScope = true;
+  }
+
+  pushScope(Scope);
+  ReaderParent->addElement(Scope);
+}
+
+void LVLogicalVisitor::addElement(LVSymbol *Symbol) {
+  ReaderScope->addElement(Symbol);
+}
+
+void LVLogicalVisitor::addElement(LVType *Type) {
+  ReaderScope->addElement(Type);
+}
+
+LVElement *LVLogicalVisitor::createElement(TypeLeafKind Kind) {
+  CurrentScope = nullptr;
+  CurrentSymbol = nullptr;
+  CurrentType = nullptr;
+
+  if (Kind < TypeIndex::FirstNonSimpleIndex) {
+    CurrentType = Reader->createType();
+    CurrentType->setIsBase();
+    CurrentType->setTag(dwarf::DW_TAG_base_type);
+    if (options().getAttributeBase())
+      CurrentType->setIncludeInPrint();
+    return CurrentType;
+  }
+
+  switch (Kind) {
+  // Types.
+  case TypeLeafKind::LF_ENUMERATE:
+    CurrentType = Reader->createTypeEnumerator();
+    CurrentType->setTag(dwarf::DW_TAG_enumerator);
+    return CurrentType;
+  case TypeLeafKind::LF_MODIFIER:
+    CurrentType = Reader->createType();
+    CurrentType->setIsModifier();
+    return CurrentType;
+  case TypeLeafKind::LF_POINTER:
+    CurrentType = Reader->createType();
+    CurrentType->setIsPointer();
+    CurrentType->setName("*");
+    CurrentType->setTag(dwarf::DW_TAG_pointer_type);
+    return CurrentType;
+
+    // Symbols.
+  case TypeLeafKind::LF_BCLASS:
+  case TypeLeafKind::LF_IVBCLASS:
+  case TypeLeafKind::LF_VBCLASS:
+    CurrentSymbol = Reader->createSymbol();
+    CurrentSymbol->setTag(dwarf::DW_TAG_inheritance);
+    CurrentSymbol->setIsInheritance();
+    return CurrentSymbol;
+  case TypeLeafKind::LF_MEMBER:
+  case TypeLeafKind::LF_STMEMBER:
+    CurrentSymbol = Reader->createSymbol();
+    CurrentSymbol->setIsMember();
+    CurrentSymbol->setTag(dwarf::DW_TAG_member);
+    return CurrentSymbol;
+
+  // Scopes.
+  case TypeLeafKind::LF_ARRAY:
+    CurrentScope = Reader->createScopeArray();
+    CurrentScope->setTag(dwarf::DW_TAG_array_type);
+    return CurrentScope;
+  case TypeLeafKind::LF_CLASS:
+    CurrentScope = Reader->createScopeAggregate();
+    CurrentScope->setTag(dwarf::DW_TAG_class_type);
+    CurrentScope->setIsClass();
+    return CurrentScope;
+  case TypeLeafKind::LF_ENUM:
+    CurrentScope = Reader->createScopeEnumeration();
+    CurrentScope->setTag(dwarf::DW_TAG_enumeration_type);
+    return CurrentScope;
+  case TypeLeafKind::LF_METHOD:
+  case TypeLeafKind::LF_ONEMETHOD:
+  case TypeLeafKind::LF_PROCEDURE:
+    CurrentScope = Reader->createScopeFunction();
+    CurrentScope->setIsSubprogram();
+    CurrentScope->setTag(dwarf::DW_TAG_subprogram);
+    return CurrentScope;
+  case TypeLeafKind::LF_STRUCTURE:
+    CurrentScope = Reader->createScopeAggregate();
+    CurrentScope->setIsStructure();
+    CurrentScope->setTag(dwarf::DW_TAG_structure_type);
+    return CurrentScope;
+  case TypeLeafKind::LF_UNION:
+    CurrentScope = Reader->createScopeAggregate();
+    CurrentScope->setIsUnion();
+    CurrentScope->setTag(dwarf::DW_TAG_union_type);
+    return CurrentScope;
+  default:
+    // If '--internal=tag' and '--print=warning' are specified in the command
+    // line, we record and print each seen 'TypeLeafKind'.
+    break;
+  }
+  return nullptr;
+}
+
+LVElement *LVLogicalVisitor::createElement(SymbolKind Kind) {
+  CurrentScope = nullptr;
+  CurrentSymbol = nullptr;
+  CurrentType = nullptr;
+  switch (Kind) {
+  // Types.
+  case SymbolKind::S_UDT:
+    CurrentType = Reader->createTypeDefinition();
+    CurrentType->setTag(dwarf::DW_TAG_typedef);
+    return CurrentType;
+
+  // Symbols.
+  case SymbolKind::S_CONSTANT:
+    CurrentSymbol = Reader->createSymbol();
+    CurrentSymbol->setIsConstant();
+    CurrentSymbol->setTag(dwarf::DW_TAG_constant);
+    return CurrentSymbol;
+
+  case SymbolKind::S_BPREL32:
+  case SymbolKind::S_REGREL32:
+  case SymbolKind::S_GDATA32:
+  case SymbolKind::S_LDATA32:
+  case SymbolKind::S_LOCAL:
+    // During the symbol traversal more information is available to
+    // determine if the symbol is a parameter or a variable. At this
+    // stage mark it as variable.
+    CurrentSymbol = Reader->createSymbol();
+    CurrentSymbol->setIsVariable();
+    CurrentSymbol->setTag(dwarf::DW_TAG_variable);
+    return CurrentSymbol;
+
+  // Scopes.
+  case SymbolKind::S_BLOCK32:
+    CurrentScope = Reader->createScope();
+    CurrentScope->setIsLexicalBlock();
+    CurrentScope->setTag(dwarf::DW_TAG_lexical_block);
+    return CurrentScope;
+  case SymbolKind::S_COMPILE2:
+  case SymbolKind::S_COMPILE3:
+    CurrentScope = Reader->createScopeCompileUnit();
+    CurrentScope->setTag(dwarf::DW_TAG_compile_unit);
+    Reader->setCompileUnit(static_cast<LVScopeCompileUnit *>(CurrentScope));
+    return CurrentScope;
+  case SymbolKind::S_INLINESITE:
+  case SymbolKind::S_INLINESITE2:
+    CurrentScope = Reader->createScopeFunctionInlined();
+    CurrentScope->setIsInlinedFunction();
+    CurrentScope->setTag(dwarf::DW_TAG_inlined_subroutine);
+    return CurrentScope;
+  case SymbolKind::S_LPROC32:
+  case SymbolKind::S_GPROC32:
+  case SymbolKind::S_LPROC32_ID:
+  case SymbolKind::S_GPROC32_ID:
+  case SymbolKind::S_SEPCODE:
+  case SymbolKind::S_THUNK32:
+    CurrentScope = Reader->createScopeFunction();
+    CurrentScope->setIsSubprogram();
+    CurrentScope->setTag(dwarf::DW_TAG_subprogram);
+    return CurrentScope;
+  default:
+    // If '--internal=tag' and '--print=warning' are specified in the command
+    // line, we record and print each seen 'SymbolKind'.
+    break;
+  }
+  return nullptr;
+}
+
+LVElement *LVLogicalVisitor::createElement(TypeIndex TI, TypeLeafKind Kind) {
+  LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI);
+  if (!Element) {
+    // We are dealing with a base type or pointer to a base type, which are
+    // not included explicitly in the CodeView format.
+    if (Kind < TypeIndex::FirstNonSimpleIndex) {
+      Element = createElement(Kind);
+      Element->setIsFinalized();
+      Shared->TypeRecords.add(StreamTPI, (TypeIndex)Kind, Kind, Element);
+      Element->setOffset(Kind);
+      return Element;
+    }
+    // We are dealing with a pointer to a base type.
+    if (TI.getIndex() < TypeIndex::FirstNonSimpleIndex) {
+      Element = createElement(Kind);
+      Shared->TypeRecords.add(StreamTPI, TI, Kind, Element);
+      Element->setOffset(TI.getIndex());
+      Element->setOffsetFromTypeIndex();
+      return Element;
+    }
+
+    W.printString("** Not implemented. **");
+    printTypeIndex("TypeIndex", TI, StreamTPI);
+    W.printString("TypeLeafKind", formatTypeLeafKind(Kind));
+    return nullptr;
+  }
+
+  Element->setOffset(TI.getIndex());
+  Element->setOffsetFromTypeIndex();
+  return Element;
+}
+
+void LVLogicalVisitor::createDataMember(CVMemberRecord &Record, LVScope *Parent,
+                                        StringRef Name, TypeIndex TI,
+                                        MemberAccess Access) {
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", TI, StreamTPI);
+    W.printString("TypeName", Name);
+  });
+
+  createElement(Record.Kind);
+  if (LVSymbol *Symbol = CurrentSymbol) {
+    Symbol->setName(Name);
+    if (TI.isNoneType() || TI.isSimple())
+      Symbol->setType(getElement(StreamTPI, TI));
+    else {
+      LazyRandomTypeCollection &Types = types();
+      CVType CVMemberType = Types.getType(TI);
+      if (CVMemberType.kind() == LF_BITFIELD) {
+        if (Error Err = finishVisitation(CVMemberType, TI, Symbol)) {
+          consumeError(std::move(Err));
+          return;
+        }
+      } else
+        Symbol->setType(getElement(StreamTPI, TI));
+    }
+    Symbol->setAccessibilityCode(Access);
+    Parent->addElement(Symbol);
+  }
+}
+
+LVSymbol *LVLogicalVisitor::createParameter(LVElement *Element, StringRef Name,
+                                            LVScope *Parent) {
+  LVSymbol *Parameter = Reader->createSymbol();
+  Parent->addElement(Parameter);
+  Parameter->setIsParameter();
+  Parameter->setTag(dwarf::DW_TAG_formal_parameter);
+  Parameter->setName(Name);
+  Parameter->setType(Element);
+  return Parameter;
+}
+
+LVSymbol *LVLogicalVisitor::createParameter(TypeIndex TI, StringRef Name,
+                                            LVScope *Parent) {
+  return createParameter(getElement(StreamTPI, TI), Name, Parent);
+}
+
+LVType *LVLogicalVisitor::createBaseType(TypeIndex TI, StringRef TypeName) {
+  TypeLeafKind SimpleKind = (TypeLeafKind)TI.getSimpleKind();
+  TypeIndex TIR = (TypeIndex)SimpleKind;
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", TIR, StreamTPI);
+    W.printString("TypeName", TypeName);
+  });
+
+  if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TIR))
+    return static_cast<LVType *>(Element);
+
+  if (createElement(TIR, SimpleKind)) {
+    CurrentType->setName(TypeName);
+    Reader->getCompileUnit()->addElement(CurrentType);
+  }
+  return CurrentType;
+}
+
+LVType *LVLogicalVisitor::createPointerType(TypeIndex TI, StringRef TypeName) {
+  LLVM_DEBUG({
+    printTypeIndex("TypeIndex", TI, StreamTPI);
+    W.printString("TypeName", TypeName);
+  });
+
+  if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI))
+    return static_cast<LVType *>(Element);
+
+  LVType *Pointee = createBaseType(TI, TypeName.drop_back(1));
+  if (createElement(TI, TypeLeafKind::LF_POINTER)) {
+    CurrentType->setIsFinalized();
+    CurrentType->setType(Pointee);
+    Reader->getCompileUnit()->addElement(CurrentType);
+  }
+  return CurrentType;
+}
+
+void LVLogicalVisitor::createParents(StringRef ScopedName, LVElement *Element) {
+  // For the given test case:
+  //
+  // struct S { enum E { ... }; };
+  // S::E V;
+  //
+  //      0 | S_LOCAL `V`
+  //          type=0x1004 (S::E), flags = none
+  // 0x1004 | LF_ENUM  `S::E`
+  //          options: has unique name | is nested
+  // 0x1009 | LF_STRUCTURE `S`
+  //          options: contains nested class
+  //
+  // When the local 'V' is processed, its type 'E' is created. But There is
+  // no direct reference to its parent 'S'. We use the scoped name for 'E',
+  // to create its parents.
+
+  // The input scoped name must have at least parent and nested names.
+  // Drop the last element name, as it corresponds to the nested type.
+  LVStringRefs Components = getAllLexicalComponents(ScopedName);
+  if (Components.size() < 2)
+    return;
+  Components.pop_back();
+
+  LVStringRefs::size_type FirstNamespace;
+  LVStringRefs::size_type FirstAggregate;
+  std::tie(FirstNamespace, FirstAggregate) =
+      Shared->NamespaceDeduction.find(Components);
+
+  LLVM_DEBUG({
+    W.printString("First Namespace", Components[FirstNamespace]);
+    W.printString("First NonNamespace", Components[FirstAggregate]);
+  });
+
+  // Create any referenced namespaces.
+  if (FirstNamespace < FirstAggregate) {
+    Shared->NamespaceDeduction.get(
+        LVStringRefs(Components.begin() + FirstNamespace,
+                     Components.begin() + FirstAggregate));
+  }
+
+  // Traverse the enclosing scopes (aggregates) and create them. In the
+  // case of nested empty aggregates, MSVC does not emit a full record
+  // description. It emits only the reference record.
+  LVScope *Aggregate = nullptr;
+  TypeIndex TIAggregate;
+  std::string AggregateName = getScopedName(
+      LVStringRefs(Components.begin(), Components.begin() + FirstAggregate));
+
+  // This traversal is executed at least once.
+  for (LVStringRefs::size_type Index = FirstAggregate;
+       Index < Components.size(); ++Index) {
+    AggregateName = getScopedName(LVStringRefs(Components.begin() + Index,
+                                               Components.begin() + Index + 1),
+                                  AggregateName);
+    TIAggregate = Shared->ForwardReferences.remap(
+        Shared->TypeRecords.find(StreamTPI, AggregateName));
+    Aggregate =
+        TIAggregate.isNoneType()
+            ? nullptr
+            : static_cast<LVScope *>(getElement(StreamTPI, TIAggregate));
+  }
+
+  // Workaround for cases where LF_NESTTYPE is missing for nested templates.
+  // If we manage to get parent information from the scoped name, we can add
+  // the nested type without relying on the LF_NESTTYPE.
+  if (Aggregate && !Element->getIsScopedAlready()) {
+    Aggregate->addElement(Element);
+    Element->setIsScopedAlready();
+  }
+}
+
+LVElement *LVLogicalVisitor::getElement(uint32_t StreamIdx, TypeIndex TI,
+                                        LVScope *Parent) {
+  LLVM_DEBUG({ printTypeIndex("TypeIndex", TI, StreamTPI); });
+  TI = Shared->ForwardReferences.remap(TI);
+  LLVM_DEBUG({ printTypeIndex("TypeIndex Remap", TI, StreamTPI); });
+
+  LVElement *Element = Shared->TypeRecords.find(StreamIdx, TI);
+  if (!Element) {
+    if (TI.isNoneType() || TI.isSimple()) {
+      StringRef TypeName = TypeIndex::simpleTypeName(TI);
+      // If the name ends with "*", create 2 logical types: a pointer and a
+      // pointee type. TypeIndex is composed of a SympleTypeMode byte followed
+      // by a SimpleTypeKind byte. The logical pointer will be identified by
+      // the full TypeIndex value and the pointee by the SimpleTypeKind.
+      return (TypeName.back() == '*') ? createPointerType(TI, TypeName)
+                                      : createBaseType(TI, TypeName);
+    }
+
+    LLVM_DEBUG({ W.printHex("TypeIndex not implemented: ", TI.getIndex()); });
+    return nullptr;
+  }
+
+  // The element has been finalized.
+  if (Element->getIsFinalized())
+    return Element;
+
+  // Add the element in case of a given parent.
+  if (Parent)
+    Parent->addElement(Element);
+
+  // Check for a composite type.
+  LazyRandomTypeCollection &Types = types();
+  CVType CVRecord = Types.getType(TI);
+  if (Error Err = finishVisitation(CVRecord, TI, Element)) {
+    consumeError(std::move(Err));
+    return nullptr;
+  }
+  Element->setIsFinalized();
+  return Element;
+}
+
+void LVLogicalVisitor::processLines() {
+  // Traverse the collected LF_UDT_SRC_LINE records and add the source line
+  // information to the logical elements.
+  for (const TypeIndex &Entry : Shared->LineRecords) {
+    CVType CVRecord = ids().getType(Entry);
+    UdtSourceLineRecord Line;
+    if (Error Err = TypeDeserializer::deserializeAs(
+            const_cast<CVType &>(CVRecord), Line))
+      consumeError(std::move(Err));
+    else {
+      LLVM_DEBUG({
+        printTypeIndex("UDT", Line.getUDT(), StreamIPI);
+        printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI);
+        W.printNumber("LineNumber", Line.getLineNumber());
+      });
+
+      // The TypeIndex returned by 'getUDT()' must point to an already
+      // created logical element. If no logical element is found, it means
+      // the LF_UDT_SRC_LINE is associated with a system TypeIndex.
+      if (LVElement *Element = Shared->TypeRecords.find(
+              StreamTPI, Line.getUDT(), /*Create=*/false)) {
+        Element->setLineNumber(Line.getLineNumber());
+        Element->setFilenameIndex(
+            Shared->StringRecords.findIndex(Line.getSourceFile()));
+      }
+    }
+  }
+}
+
+void LVLogicalVisitor::processNamespaces() {
+  // Create namespaces.
+  Shared->NamespaceDeduction.init();
+}
+
+void LVLogicalVisitor::processFiles() { Shared->StringRecords.addFilenames(); }
+
+void LVLogicalVisitor::printRecords(raw_ostream &OS) const {
+  if (!options().getInternalTag())
+    return;
+
+  unsigned Count = 0;
+  auto PrintItem = [&](StringRef Name) {
+    auto NewLine = [&]() {
+      if (++Count == 4) {
+        Count = 0;
+        OS << "\n";
+      }
+    };
+    OS << format("%20s", Name.str().c_str());
+    NewLine();
+  };
+
+  OS << "\nTypes:\n";
+  for (const TypeLeafKind &Kind : Shared->TypeKinds)
+    PrintItem(formatTypeLeafKind(Kind));
+  Shared->TypeKinds.clear();
+
+  Count = 0;
+  OS << "\nSymbols:\n";
+  for (const SymbolKind &Kind : Shared->SymbolKinds)
+    PrintItem(LVCodeViewReader::getSymbolKindName(Kind));
+  Shared->SymbolKinds.clear();
+
+  OS << "\n";
+}
+
+Error LVLogicalVisitor::inlineSiteAnnotation(LVScope *AbstractFunction,
+                                             LVScope *InlinedFunction,
+                                             InlineSiteSym &InlineSite) {
+  // Get the parent scope to update the address ranges of the nested
+  // scope representing the inlined function.
+  LVAddress ParentLowPC = 0;
+  LVScope *Parent = InlinedFunction->getParentScope();
+  if (const LVLocations *Locations = Parent->getRanges()) {
+    if (!Locations->empty())
+      ParentLowPC = (*Locations->begin())->getLowerAddress();
+  }
+
+  // For the given inlinesite, get the initial line number and its
+  // source filename. Update the logical scope representing it.
+  uint32_t LineNumber = 0;
+  StringRef Filename;
+  LVInlineeInfo::iterator Iter = InlineeInfo.find(InlineSite.Inlinee);
+  if (Iter != InlineeInfo.end()) {
+    LineNumber = Iter->second.first;
+    Filename = Iter->second.second;
+    AbstractFunction->setLineNumber(LineNumber);
+    // TODO: This part needs additional work in order to set properly the
+    // correct filename in order to detect changes between filenames.
+    // AbstractFunction->setFilename(Filename);
+  }
+
+  LLVM_DEBUG({
+    dbgs() << "inlineSiteAnnotation\n"
+           << "Abstract: " << AbstractFunction->getName() << "\n"
+           << "Inlined: " << InlinedFunction->getName() << "\n"
+           << "Parent: " << Parent->getName() << "\n"
+           << "Low PC: " << hexValue(ParentLowPC) << "\n";
+  });
+
+  // Get the source lines if requested by command line option.
+  if (!options().getPrintLines())
+    return Error::success();
+
+  // Limitation: Currently we don't track changes in the FileOffset. The
+  // side effects are the caller that it is unable to 
diff erentiate the
+  // source filename for the inlined code.
+  uint64_t CodeOffset = ParentLowPC;
+  int32_t LineOffset = LineNumber;
+  uint32_t FileOffset = 0;
+
+  auto UpdateClose = [&]() { LLVM_DEBUG({ dbgs() << ("\n"); }); };
+  auto UpdateCodeOffset = [&](uint32_t Delta) {
+    CodeOffset += Delta;
+    LLVM_DEBUG({
+      dbgs() << formatv(" code 0x{0} (+0x{1})", utohexstr(CodeOffset),
+                        utohexstr(Delta));
+    });
+  };
+  auto UpdateLineOffset = [&](int32_t Delta) {
+    LineOffset += Delta;
+    LLVM_DEBUG({
+      char Sign = Delta > 0 ? '+' : '-';
+      dbgs() << formatv(" line {0} ({1}{2})", LineOffset, Sign,
+                        std::abs(Delta));
+    });
+  };
+  auto UpdateFileOffset = [&](int32_t Offset) {
+    FileOffset = Offset;
+    LLVM_DEBUG({ dbgs() << formatv(" file {0}", FileOffset); });
+  };
+
+  LVLines InlineeLines;
+  auto CreateLine = [&]() {
+    // Create the logical line record.
+    LVLineDebug *Line = Reader->createLineDebug();
+    Line->setAddress(CodeOffset);
+    Line->setLineNumber(LineOffset);
+    // TODO: This part needs additional work in order to set properly the
+    // correct filename in order to detect changes between filenames.
+    // Line->setFilename(Filename);
+    InlineeLines.push_back(Line);
+  };
+
+  bool SeenLowAddress = false;
+  bool SeenHighAddress = false;
+  uint64_t LowPC = 0;
+  uint64_t HighPC = 0;
+
+  for (auto &Annot : InlineSite.annotations()) {
+    LLVM_DEBUG({
+      dbgs() << formatv("  {0}",
+                        fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9));
+    });
+
+    // Use the opcode to interpret the integer values.
+    switch (Annot.OpCode) {
+    case BinaryAnnotationsOpCode::ChangeCodeOffset:
+    case BinaryAnnotationsOpCode::CodeOffset:
+    case BinaryAnnotationsOpCode::ChangeCodeLength:
+      UpdateCodeOffset(Annot.U1);
+      UpdateClose();
+      if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeOffset) {
+        CreateLine();
+        LowPC = CodeOffset;
+        SeenLowAddress = true;
+        break;
+      }
+      if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeLength) {
+        HighPC = CodeOffset - 1;
+        SeenHighAddress = true;
+      }
+      break;
+    case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset:
+      UpdateCodeOffset(Annot.U2);
+      UpdateClose();
+      break;
+    case BinaryAnnotationsOpCode::ChangeLineOffset:
+    case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset:
+      UpdateCodeOffset(Annot.U1);
+      UpdateLineOffset(Annot.S1);
+      UpdateClose();
+      if (Annot.OpCode ==
+          BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset)
+        CreateLine();
+      break;
+    case BinaryAnnotationsOpCode::ChangeFile:
+      UpdateFileOffset(Annot.U1);
+      UpdateClose();
+      break;
+    default:
+      break;
+    }
+    if (SeenLowAddress && SeenHighAddress) {
+      SeenLowAddress = false;
+      SeenHighAddress = false;
+      InlinedFunction->addObject(LowPC, HighPC);
+    }
+  }
+
+  Reader->addInlineeLines(InlinedFunction, InlineeLines);
+  UpdateClose();
+
+  return Error::success();
+}

diff  --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp
index 009cd113f6526..081cede6d840d 100644
--- a/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp
@@ -240,7 +240,9 @@ Error DbiModuleList::initializeFileInfo(BinaryStreamRef FileInfo) {
 }
 
 uint32_t DbiModuleList::getModuleCount() const {
-  return FileInfoHeader->NumModules;
+  // Workaround to avoid the crash until upstream issue is fixed:
+  // https://github.com/llvm/llvm-project/issues/55214
+  return FileInfoHeader ? FileInfoHeader->NumModules : 0;
 }
 
 uint32_t DbiModuleList::getSourceFileCount() const {

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test
new file mode 100644
index 0000000000000..9de2c2fd5062f
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test
@@ -0,0 +1,103 @@
+; REQUIRES: x86-registered-target
+
+; Test case 1 - General options
+
+; test.cpp
+;  1  using INTPTR = const int *;
+;  2  int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
+;  3    if (ParamBool) {
+;  4      typedef int INTEGER;
+;  5      const INTEGER CONSTANT = 7;
+;  6      return CONSTANT;
+;  7    }
+;  8    return ParamUnsigned;
+;  9  }
+
+; Compare mode - Logical view.
+; The output shows in view form the 'missing (-), added (+)' elements,
+; giving more context by swapping the reference and target object files.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level \
+; RUN:                         --compare=types \
+; RUN:                         --report=view \
+; RUN:                         --print=symbols,types \
+; RUN:                         %p/Inputs/test-codeview-clang.o \
+; RUN:                         %p/Inputs/test-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Reference: 'test-codeview-clang.o'
+; ONE-NEXT: Target:    'test-codeview-msvc.o'
+; ONE-EMPTY:
+; ONE-NEXT: Logical View:
+; ONE-NEXT:  [000]           {File} 'test-codeview-clang.o'
+; ONE-EMPTY:
+; ONE-NEXT:  [001]             {CompileUnit} 'test.cpp'
+; ONE-NEXT:  [002]               {TypeAlias} 'INTPTR' -> '* const int'
+; ONE-NEXT:  [002]               {Function} extern not_inlined 'foo' -> 'int'
+; ONE-NEXT: -[003]                 {TypeAlias} 'INTEGER' -> 'int'
+; ONE-NEXT:  [003]                 {Parameter} 'ParamBool' -> 'bool'
+; ONE-NEXT:  [003]                 {Parameter} 'ParamPtr' -> '* const int'
+; ONE-NEXT:  [003]                 {Parameter} 'ParamUnsigned' -> 'unsigned'
+; ONE-NEXT:  [003]                 {Block}
+; ONE-NEXT:  [004]                   {Variable} 'CONSTANT' -> 'const int'
+; ONE-NEXT: +[004]                   {TypeAlias} 'INTEGER' -> 'int'
+
+; Compare mode - Logical elements.
+; The output shows in tabular form the 'missing (-), added (+)' elements,
+; giving more context by swapping the reference and target object files.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level \
+; RUN:                         --compare=types \
+; RUN:                         --report=list \
+; RUN:                         --print=symbols,types,summary \
+; RUN:                         %p/Inputs/test-codeview-clang.o \
+; RUN:                         %p/Inputs/test-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s
+
+; TWO:      Reference: 'test-codeview-clang.o'
+; TWO-NEXT: Target:    'test-codeview-msvc.o'
+; TWO-EMPTY:
+; TWO-NEXT: (1) Missing Types:
+; TWO-NEXT: -[003]           {TypeAlias} 'INTEGER' -> 'int'
+; TWO-EMPTY:
+; TWO-NEXT: (1) Added Types:
+; TWO-NEXT: +[004]           {TypeAlias} 'INTEGER' -> 'int'
+; TWO-EMPTY:
+; TWO-NEXT: ----------------------------------------
+; TWO-NEXT: Element   Expected    Missing      Added
+; TWO-NEXT: ----------------------------------------
+; TWO-NEXT: Scopes           4          0          0
+; TWO-NEXT: Symbols          0          0          0
+; TWO-NEXT: Types            2          1          1
+; TWO-NEXT: Lines            0          0          0
+; TWO-NEXT: ----------------------------------------
+; TWO-NEXT: Total            6          1          1
+
+; Changing the 'Reference' and 'Target' order:
+
+; RUN: llvm-debuginfo-analyzer --attribute=level \
+; RUN:                         --compare=types \
+; RUN:                         --report=list \
+; RUN:                         --print=symbols,types,summary \
+; RUN:                         %p/Inputs/test-codeview-msvc.o \
+; RUN:                         %p/Inputs/test-codeview-clang.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=THR %s
+
+; THR:      Reference: 'test-codeview-msvc.o'
+; THR-NEXT: Target:    'test-codeview-clang.o'
+; THR-EMPTY:
+; THR-NEXT: (1) Missing Types:
+; THR-NEXT: -[004]           {TypeAlias} 'INTEGER' -> 'int'
+; THR-EMPTY:
+; THR-NEXT: (1) Added Types:
+; THR-NEXT: +[003]           {TypeAlias} 'INTEGER' -> 'int'
+; THR-EMPTY:
+; THR-NEXT: ----------------------------------------
+; THR-NEXT: Element   Expected    Missing      Added
+; THR-NEXT: ----------------------------------------
+; THR-NEXT: Scopes           4          0          0
+; THR-NEXT: Symbols          0          0          0
+; THR-NEXT: Types            2          1          1
+; THR-NEXT: Lines            0          0          0
+; THR-NEXT: ----------------------------------------
+; THR-NEXT: Total            6          1          1

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test
new file mode 100644
index 0000000000000..be2085a187eb9
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test
@@ -0,0 +1,65 @@
+; REQUIRES: x86-registered-target
+
+; Test case 1 - General options.
+
+; test.cpp
+;  1  using INTPTR = const int *;
+;  2  int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
+;  3    if (ParamBool) {
+;  4      typedef int INTEGER;
+;  5      const INTEGER CONSTANT = 7;
+;  6      return CONSTANT;
+;  7    }
+;  8    return ParamUnsigned;
+;  9  }
+
+; Print basic details.
+; The following command prints basic details for all the logical elements
+; sorted by the debug information internal offset; it includes its lexical
+; level and debug info format.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format \
+; RUN:                         --output-sort=offset \
+; RUN:                         --print=scopes,symbols,types,lines,instructions \
+; RUN:                         %p/Inputs/test-codeview-clang.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format \
+; RUN:                         --output-sort=offset \
+; RUN:                         --print=elements \
+; RUN:                         %p/Inputs/test-codeview-clang.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [000]           {File} 'test-codeview-clang.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'test.cpp'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'foo' -> 'int'
+; ONE-NEXT: [003]                 {Parameter} 'ParamPtr' -> '* const int'
+; ONE-NEXT: [003]                 {Parameter} 'ParamUnsigned' -> 'unsigned'
+; ONE-NEXT: [003]                 {Parameter} 'ParamBool' -> 'bool'
+; ONE-NEXT: [003]                 {Block}
+; ONE-NEXT: [004]                   {Variable} 'CONSTANT' -> 'const int'
+; ONE-NEXT: [004]     5             {Line}
+; ONE-NEXT: [004]                   {Code} 'movl	$0x7, 0x4(%rsp)'
+; ONE-NEXT: [004]     6             {Line}
+; ONE-NEXT: [004]                   {Code} 'movl	$0x7, 0x1c(%rsp)'
+; ONE-NEXT: [004]                   {Code} 'jmp	0x8'
+; ONE-NEXT: [003]                 {TypeAlias} 'INTEGER' -> 'int'
+; ONE-NEXT: [003]     2           {Line}
+; ONE-NEXT: [003]                 {Code} 'subq	$0x20, %rsp'
+; ONE-NEXT: [003]                 {Code} 'andb	$0x1, %r8b'
+; ONE-NEXT: [003]                 {Code} 'movb	%r8b, 0x1b(%rsp)'
+; ONE-NEXT: [003]                 {Code} 'movl	%edx, 0x14(%rsp)'
+; ONE-NEXT: [003]                 {Code} 'movq	%rcx, 0x8(%rsp)'
+; ONE-NEXT: [003]     3           {Line}
+; ONE-NEXT: [003]                 {Code} 'testb	$0x1, 0x1b(%rsp)'
+; ONE-NEXT: [003]                 {Code} 'je	0x15'
+; ONE-NEXT: [003]     8           {Line}
+; ONE-NEXT: [003]                 {Code} 'movl	0x14(%rsp), %eax'
+; ONE-NEXT: [003]                 {Code} 'movl	%eax, 0x1c(%rsp)'
+; ONE-NEXT: [003]     9           {Line}
+; ONE-NEXT: [003]                 {Code} 'movl	0x1c(%rsp), %eax'
+; ONE-NEXT: [003]                 {Code} 'addq	$0x20, %rsp'
+; ONE-NEXT: [003]                 {Code} 'retq'
+; ONE-NEXT: [002]               {TypeAlias} 'INTPTR' -> '* const int'

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test
new file mode 100644
index 0000000000000..49142726cf585
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test
@@ -0,0 +1,70 @@
+; REQUIRES: x86-registered-target
+
+; Test case 1 - General options
+
+; test.cpp
+;  1  using INTPTR = const int *;
+;  2  int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
+;  3    if (ParamBool) {
+;  4      typedef int INTEGER;
+;  5      const INTEGER CONSTANT = 7;
+;  6      return CONSTANT;
+;  7    }
+;  8    return ParamUnsigned;
+;  9  }
+
+; Select logical elements.
+; The following prints all 'instructions', 'symbols' and 'types' that
+; contain 'inte' or 'movl' in their names or types, using a tab layout
+; and given the number of matches.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level \
+; RUN:                         --select-nocase --select-regex \
+; RUN:                         --select=INTe --select=movl \
+; RUN:                         --report=list \
+; RUN:                         --print=symbols,types,instructions,summary \
+; RUN:                         %p/Inputs/test-codeview-clang.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [000]           {File} 'test-codeview-clang.o'
+; ONE-EMPTY:
+; ONE-NEXT: [001]           {CompileUnit} 'test.cpp'
+; ONE-NEXT: [003]           {TypeAlias} 'INTEGER' -> 'int'
+; ONE-NEXT: [004]           {Code} 'movl	$0x7, 0x1c(%rsp)'
+; ONE-NEXT: [004]           {Code} 'movl	$0x7, 0x4(%rsp)'
+; ONE-NEXT: [003]           {Code} 'movl	%eax, 0x1c(%rsp)'
+; ONE-NEXT: [003]           {Code} 'movl	%edx, 0x14(%rsp)'
+; ONE-NEXT: [003]           {Code} 'movl	0x14(%rsp), %eax'
+; ONE-NEXT: [003]           {Code} 'movl	0x1c(%rsp), %eax'
+; ONE-EMPTY:
+; ONE-NEXT: -----------------------------
+; ONE-NEXT: Element      Total    Printed
+; ONE-NEXT: -----------------------------
+; ONE-NEXT: Scopes           3          0
+; ONE-NEXT: Symbols          4          0
+; ONE-NEXT: Types            2          1
+; ONE-NEXT: Lines           21          6
+; ONE-NEXT: -----------------------------
+; ONE-NEXT: Total           30          7
+
+; RUN: llvm-debuginfo-analyzer --attribute=level \
+; RUN:                         --select-regex --select-nocase \
+; RUN:                         --select=INTe \
+; RUN:                         --report=list \
+; RUN:                         --print=symbols,types \
+; RUN:                         %p/Inputs/test-codeview-clang.o \
+; RUN:                         %p/Inputs/test-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s
+
+; TWO:      Logical View:
+; TWO-NEXT: [000]           {File} 'test-codeview-clang.o'
+; TWO-EMPTY:
+; TWO-NEXT: [001]           {CompileUnit} 'test.cpp'
+; TWO-NEXT: [003]           {TypeAlias} 'INTEGER' -> 'int'
+; TWO-EMPTY:
+; TWO-NEXT: Logical View:
+; TWO-NEXT: [000]           {File} 'test-codeview-msvc.o'
+; TWO-EMPTY:
+; TWO-NEXT: [001]           {CompileUnit} 'test.cpp'
+; TWO-NEXT: [004]           {TypeAlias} 'INTEGER' -> 'int'

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test
new file mode 100644
index 0000000000000..d2058c6b6be32
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test
@@ -0,0 +1,56 @@
+; REQUIRES: x86-registered-target
+
+; Test case 2 - Assembler instructions.
+
+; hello-world.cpp
+;  1  extern int printf(const char * format, ... );
+;  2
+;  3  int main()
+;  4  {
+;  5    printf("Hello, World\n");
+;  6    return 0;
+;  7  }
+
+; Logical lines.
+; The logical views shows the intermixed lines and assembler instructions,
+; allowing to compare the code generated by the 
diff erent toolchains.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
+; RUN:                         --print=lines,instructions \
+; RUN:                         %p/Inputs/hello-world-codeview-clang.o \
+; RUN:                         %p/Inputs/hello-world-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [000]           {File} 'hello-world-codeview-clang.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'hello-world.cpp'
+; ONE-NEXT: [002]               {Producer} 'clang version 15.0.0 {{.*}}'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'main' -> 'int'
+; ONE-NEXT: [003]     4           {Line}
+; ONE-NEXT: [003]                 {Code} 'subq	$0x28, %rsp'
+; ONE-NEXT: [003]                 {Code} 'movl	$0x0, 0x24(%rsp)'
+; ONE-NEXT: [003]     5           {Line}
+; ONE-NEXT: [003]                 {Code} 'leaq	(%rip), %rcx'
+; ONE-NEXT: [003]                 {Code} 'callq	0x0'
+; ONE-NEXT: [003]     6           {Line}
+; ONE-NEXT: [003]                 {Code} 'xorl	%eax, %eax'
+; ONE-NEXT: [003]                 {Code} 'addq	$0x28, %rsp'
+; ONE-NEXT: [003]                 {Code} 'retq'
+; ONE-EMPTY:
+; ONE-NEXT: Logical View:
+; ONE-NEXT: [000]           {File} 'hello-world-codeview-msvc.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'hello-world.cpp'
+; ONE-NEXT: [002]               {Producer} 'Microsoft (R) Optimizing Compiler'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'main' -> 'int'
+; ONE-NEXT: [003]     4           {Line}
+; ONE-NEXT: [003]                 {Code} 'subq	$0x28, %rsp'
+; ONE-NEXT: [003]     5           {Line}
+; ONE-NEXT: [003]                 {Code} 'leaq	(%rip), %rcx'
+; ONE-NEXT: [003]                 {Code} 'callq	0x0'
+; ONE-NEXT: [003]     6           {Line}
+; ONE-NEXT: [003]                 {Code} 'xorl	%eax, %eax'
+; ONE-NEXT: [003]     7           {Line}
+; ONE-NEXT: [003]                 {Code} 'addq	$0x28, %rsp'
+; ONE-NEXT: [003]                 {Code} 'retq'

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test
new file mode 100644
index 0000000000000..b7c646a103019
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test
@@ -0,0 +1,107 @@
+; REQUIRES: x86-registered-target
+
+; Test case 3 - Incorrect lexical scope for typedef.
+
+; pr-44884.cpp
+;  1  int bar(float Input) { return (int)Input; }
+;  2
+;  3  unsigned foo(char Param) {
+;  4    typedef int INT;                // ** Definition for INT **
+;  5    INT Value = Param;
+;  6    {
+;  7      typedef float FLOAT;          // ** Definition for FLOAT **
+;  8      {
+;  9        FLOAT Added = Value + Param;
+; 10        Value = bar(Added);
+; 11      }
+; 12    }
+; 13    return Value + Param;
+; 14  }
+
+; The lines 4 and 7 contains 2 typedefs, defined at 
diff erent lexical
+; scopes.
+
+; The above test is used to illustrates a scope issue found in the
+; Clang compiler.
+; PR44884: https://bugs.llvm.org/show_bug.cgi?id=44884
+; PR44229: https://github.com/llvm/llvm-project/issues/44229
+
+; In the following logical views, we can see that the Clang compiler
+; emits both typedefs at the same lexical scope (3), which is wrong.
+; GCC and MSVC emit correct lexical scope for both typedefs.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
+; RUN:                         --output-sort=kind \
+; RUN:                         --print=symbols,types,lines \
+; RUN:                         %p/Inputs/pr-44884-codeview-clang.o \
+; RUN:                         %p/Inputs/pr-44884-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [000]           {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'pr-44884.cpp'
+; ONE-NEXT: [002]               {Producer} 'clang version 15.0.0 {{.*}}'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'bar' -> 'int'
+; ONE-NEXT: [003]                 {Parameter} 'Input' -> 'float'
+; ONE-NEXT: [003]     1           {Line}
+; ONE-NEXT: [002]               {Function} extern not_inlined 'foo' -> 'unsigned'
+; ONE-NEXT: [003]                 {Block}
+; ONE-NEXT: [004]                   {Variable} 'Added' -> 'float'
+; ONE-NEXT: [004]     9             {Line}
+; ONE-NEXT: [004]    10             {Line}
+; ONE-NEXT: [003]                 {Parameter} 'Param' -> 'char'
+; ONE-NEXT: [003]                 {TypeAlias} 'FLOAT' -> 'float'
+; ONE-NEXT: [003]                 {TypeAlias} 'INT' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Value' -> 'int'
+; ONE-NEXT: [003]     3           {Line}
+; ONE-NEXT: [003]     5           {Line}
+; ONE-NEXT: [003]    13           {Line}
+; ONE-EMPTY:
+; ONE-NEXT: Logical View:
+; ONE-NEXT: [000]           {File} 'pr-44884-codeview-msvc.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'pr-44884.cpp'
+; ONE-NEXT: [002]               {Producer} 'Microsoft (R) Optimizing Compiler'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'bar' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Input' -> 'float'
+; ONE-NEXT: [003]     1           {Line}
+; ONE-NEXT: [002]               {Function} extern not_inlined 'foo' -> 'unsigned'
+; ONE-NEXT: [003]                 {Block}
+; ONE-NEXT: [004]                   {Block}
+; ONE-NEXT: [005]                     {Variable} 'Added' -> 'float'
+; ONE-NEXT: [004]                   {TypeAlias} 'FLOAT' -> 'float'
+; ONE-NEXT: [004]     9             {Line}
+; ONE-NEXT: [004]    10             {Line}
+; ONE-NEXT: [003]                 {TypeAlias} 'INT' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Param' -> 'char'
+; ONE-NEXT: [003]                 {Variable} 'Value' -> 'int'
+; ONE-NEXT: [003]     3           {Line}
+; ONE-NEXT: [003]     5           {Line}
+; ONE-NEXT: [003]    13           {Line}
+; ONE-NEXT: [003]    14           {Line}
+
+; Using the selection facilities, we can produce a simple tabular
+; output showing just the logical types that are 'Typedef'.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format \
+; RUN:                         --output-sort=name \
+; RUN:                         --select-types=Typedef \
+; RUN:                         --report=list \
+; RUN:                         --print=types \
+; RUN:                         %p/Inputs/pr-44884-*.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s
+
+; TWO:      Logical View:
+; TWO-NEXT: [000]           {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64
+; TWO-EMPTY:
+; TWO-NEXT: [001]           {CompileUnit} 'pr-44884.cpp'
+; TWO-NEXT: [003]           {TypeAlias} 'FLOAT' -> 'float'
+; TWO-NEXT: [003]           {TypeAlias} 'INT' -> 'int'
+; TWO-EMPTY:
+; TWO-NEXT: Logical View:
+; TWO-NEXT: [000]           {File} 'pr-44884-codeview-msvc.o' -> COFF-x86-64
+; TWO-EMPTY:
+; TWO-NEXT: [001]           {CompileUnit} 'pr-44884.cpp'
+; TWO-NEXT: [004]           {TypeAlias} 'FLOAT' -> 'float'
+; TWO-NEXT: [003]           {TypeAlias} 'INT' -> 'int'

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test
new file mode 100644
index 0000000000000..aa84621683b43
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test
@@ -0,0 +1,136 @@
+; REQUIRES: x86-registered-target
+
+; Test case 4 - Missing nested enumerations.
+
+; pr-46466.cpp
+;   1  struct Struct {
+;   2    union Union {
+;   3      enum NestedEnum { RED, BLUE };
+;   4    };
+;   5    Union U;
+;   6  };
+;   7
+;   8  Struct S;
+;   9  int test() {
+;  10    return S.U.BLUE;
+;  11  }
+
+; The above test is used to illustrate a scope issue found in the Clang
+; compiler.
+; PR46466: https://bugs.llvm.org/show_bug.cgi?id=46466
+; PR45811: https://github.com/llvm/llvm-project/issues/45811
+
+; In the following logical views, we can see that the CodeView debug
+; information generated by the Clang compiler does not include any
+; references to the enumerators 'RED' and 'BLUE'. The CodeView generated
+; by GCC and MSVC, does include such references.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
+; RUN:                         --output-sort=name \
+; RUN:                         --print=symbols,types \
+; RUN:                         %p/Inputs/pr-46466-codeview-clang.o \
+; RUN:                         %p/Inputs/pr-46466-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'pr-46466.cpp'
+; ONE-NEXT: [002]               {Producer} 'clang version 15.0.0 {{.*}}'
+; ONE-NEXT: [002]               {Variable} extern 'S' -> 'Struct'
+; ONE-NEXT: [002]     1         {Struct} 'Struct'
+; ONE-NEXT: [003]                 {Member} public 'U' -> 'Union'
+; ONE-NEXT: [003]     2           {Union} 'Union'
+; ONE-NEXT: [004]     3             {Enumeration} 'NestedEnum' -> 'int'
+; ONE-NEXT: [005]                     {Enumerator} 'BLUE' = '0x1'
+; ONE-NEXT: [005]                     {Enumerator} 'RED' = '0x0'
+; ONE-EMPTY:
+; ONE-NEXT: Logical View:
+; ONE-NEXT: [000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'pr-46466.cpp'
+; ONE-NEXT: [002]               {Producer} 'Microsoft (R) Optimizing Compiler'
+; ONE-NEXT: [002]               {Variable} extern 'S' -> 'Struct'
+; ONE-NEXT: [002]     1         {Struct} 'Struct'
+; ONE-NEXT: [003]                 {Member} public 'U' -> 'Union'
+; ONE-NEXT: [003]     2           {Union} 'Union'
+; ONE-NEXT: [004]     3             {Enumeration} 'NestedEnum' -> 'int'
+; ONE-NEXT: [005]                     {Enumerator} 'BLUE' = '0x1'
+; ONE-NEXT: [005]                     {Enumerator} 'RED' = '0x0'
+
+; Using the selection facilities, we can produce a logical view
+; showing just the logical types that are 'Enumerator' and its
+; parents. The logical view is sorted by the types name.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format \
+; RUN:                         --output-sort=name \
+; RUN:                         --select-types=Enumerator \
+; RUN:                         --report=parents \
+; RUN:                         --print=types \
+; RUN:                         %p/Inputs/pr-46466-*.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s
+
+; TWO:      Logical View:
+; TWO-NEXT: [000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64
+; TWO-EMPTY:
+; TWO-NEXT: [001]             {CompileUnit} 'pr-46466.cpp'
+; TWO-NEXT: [002]     1         {Struct} 'Struct'
+; TWO-NEXT: [003]     2           {Union} 'Union'
+; TWO-NEXT: [004]     3             {Enumeration} 'NestedEnum' -> 'int'
+; TWO-NEXT: [005]                     {Enumerator} 'BLUE' = '0x1'
+; TWO-NEXT: [005]                     {Enumerator} 'RED' = '0x0'
+; TWO-EMPTY:
+; TWO-NEXT: Logical View:
+; TWO-NEXT: [000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64
+; TWO-EMPTY:
+; TWO-NEXT: [001]             {CompileUnit} 'pr-46466.cpp'
+; TWO-NEXT: [002]     1         {Struct} 'Struct'
+; TWO-NEXT: [003]     2           {Union} 'Union'
+; TWO-NEXT: [004]     3             {Enumeration} 'NestedEnum' -> 'int'
+; TWO-NEXT: [005]                     {Enumerator} 'BLUE' = '0x1'
+; TWO-NEXT: [005]                     {Enumerator} 'RED' = '0x0'
+
+; Using the selection facilities, we can produce a simple tabular output
+; including a summary for the logical types that are 'Enumerator'. The
+; logical view is sorted by the types name.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format \
+; RUN:                         --output-sort=name \
+; RUN:                         --select-types=Enumerator \
+; RUN:                         --print=types,summary \
+; RUN:                         %p/Inputs/pr-46466-*.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=THR %s
+
+; THR:      Logical View:
+; THR-NEXT: [000]           {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64
+; THR-EMPTY:
+; THR-NEXT: [001]           {CompileUnit} 'pr-46466.cpp'
+; THR-NEXT: [005]           {Enumerator} 'BLUE' = '0x1'
+; THR-NEXT: [005]           {Enumerator} 'RED' = '0x0'
+; THR-EMPTY:
+; THR-NEXT: -----------------------------
+; THR-NEXT: Element      Total    Printed
+; THR-NEXT: -----------------------------
+; THR-NEXT: Scopes           5          0
+; THR-NEXT: Symbols          2          0
+; THR-NEXT: Types            6          2
+; THR-NEXT: Lines            0          0
+; THR-NEXT: -----------------------------
+; THR-NEXT: Total           13          2
+; THR-EMPTY:
+; THR-NEXT: Logical View:
+; THR-NEXT: [000]           {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64
+; THR-EMPTY:
+; THR-NEXT: [001]           {CompileUnit} 'pr-46466.cpp'
+; THR-NEXT: [005]           {Enumerator} 'BLUE' = '0x1'
+; THR-NEXT: [005]           {Enumerator} 'RED' = '0x0'
+; THR-EMPTY:
+; THR-NEXT: -----------------------------
+; THR-NEXT: Element      Total    Printed
+; THR-NEXT: -----------------------------
+; THR-NEXT: Scopes           5          0
+; THR-NEXT: Symbols          2          0
+; THR-NEXT: Types            7          2
+; THR-NEXT: Lines            0          0
+; THR-NEXT: -----------------------------
+; THR-NEXT: Total           14          2

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test
new file mode 100644
index 0000000000000..dea30c9f2b37d
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test
@@ -0,0 +1,97 @@
+; REQUIRES: x86-registered-target
+
+; Test case 5 - Incorrect lexical scope variable.
+
+; pr-43860.cpp
+;  1  #include "definitions.h"
+;  2  forceinline int InlineFunction(int Param) {
+;  3    int Var_1 = Param;
+;  4    {
+;  5      int Var_2 = Param + Var_1;
+;  6      Var_1 = Var_2;
+;  7    }
+;  8    return Var_1;
+;  9  }
+; 10
+; 11  int test(int Param_1, int Param_2) {
+; 12    int A = Param_1;
+; 13    A += InlineFunction(Param_2);
+; 14    return A;
+; 15  }
+
+; The above test is used to illustrate a variable issue found in the
+; Clang compiler.
+; PR43860: https://bugs.llvm.org/show_bug.cgi?id=43860
+; PR43205: https://github.com/llvm/llvm-project/issues/43205
+
+; In the following logical views, we can see that the CodeView debug
+; information generated by the Clang compiler shows the variables
+; 'Var_1' and 'Var_2' are at the same lexical scope (4) in the function
+; 'InlineFuction'.
+; The CodeView generated by MSVC, show those variables at the correct
+; lexical scope: '3' and '4' respectively.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
+; RUN:                         --output-sort=name \
+; RUN:                         --print=symbols \
+; RUN:                         %p/Inputs/pr-43860-codeview-clang.o \
+; RUN:                         %p/Inputs/pr-43860-codeview-msvc.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [000]           {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'pr-43860.cpp'
+; ONE-NEXT: [002]               {Producer} 'clang version 15.0.0 {{.*}}'
+; ONE-NEXT: [002]     2         {Function} inlined 'InlineFunction' -> 'int'
+; ONE-NEXT: [003]                 {Parameter} '' -> 'int'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'test' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'A' -> 'int'
+; ONE-NEXT: [003]                 {InlinedFunction} inlined 'InlineFunction' -> 'int'
+; ONE-NEXT: [004]                   {Parameter} 'Param' -> 'int'
+; ONE-NEXT: [004]                   {Variable} 'Var_1' -> 'int'
+; ONE-NEXT: [004]                   {Variable} 'Var_2' -> 'int'
+; ONE-NEXT: [003]                 {Parameter} 'Param_1' -> 'int'
+; ONE-NEXT: [003]                 {Parameter} 'Param_2' -> 'int'
+; ONE-EMPTY:
+; ONE-NEXT: Logical View:
+; ONE-NEXT: [000]           {File} 'pr-43860-codeview-msvc.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [001]             {CompileUnit} 'pr-43860.cpp'
+; ONE-NEXT: [002]               {Producer} 'Microsoft (R) Optimizing Compiler'
+; ONE-NEXT: [002]               {Function} extern declared_inlined 'InlineFunction' -> 'int'
+; ONE-NEXT: [003]                 {Block}
+; ONE-NEXT: [004]                   {Variable} 'Var_2' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Param' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Var_1' -> 'int'
+; ONE-NEXT: [002]               {Function} extern not_inlined 'test' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'A' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Param_1' -> 'int'
+; ONE-NEXT: [003]                 {Variable} 'Param_2' -> 'int'
+
+; Using the selection facilities, we can produce a simple tabular output
+; showing just the logical elements that have in their name the 'var'
+; pattern. The logical view is sorted by the variables name.
+
+; RUN: llvm-debuginfo-analyzer --attribute=level,format \
+; RUN:                         --output-sort=name \
+; RUN:                         --select-regex --select-nocase \
+; RUN:                         --select=Var \
+; RUN:                         --report=list \
+; RUN:                         --print=symbols \
+; RUN:                         %p/Inputs/pr-43860-*.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s
+
+; TWO:      Logical View:
+; TWO-NEXT: [000]           {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64
+; TWO-EMPTY:
+; TWO-NEXT: [001]           {CompileUnit} 'pr-43860.cpp'
+; TWO-NEXT: [004]           {Variable} 'Var_1' -> 'int'
+; TWO-NEXT: [004]           {Variable} 'Var_2' -> 'int'
+; TWO-EMPTY:
+; TWO-NEXT: Logical View:
+; TWO-NEXT: [000]           {File} 'pr-43860-codeview-msvc.o' -> COFF-x86-64
+; TWO-EMPTY:
+; TWO-NEXT: [001]           {CompileUnit} 'pr-43860.cpp'
+; TWO-NEXT: [003]           {Variable} 'Var_1' -> 'int'
+; TWO-NEXT: [004]           {Variable} 'Var_2' -> 'int'

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test
new file mode 100644
index 0000000000000..4e6083be343a0
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test
@@ -0,0 +1,99 @@
+; REQUIRES: x86-registered-target
+
+; Test case 6 - Full logical view
+
+; test.cpp
+;  1  using INTPTR = const int *;
+;  2  int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
+;  3    if (ParamBool) {
+;  4      typedef int INTEGER;
+;  5      const INTEGER CONSTANT = 7;
+;  6      return CONSTANT;
+;  7    }
+;  8    return ParamUnsigned;
+;  9  }
+
+; Print low level details.
+; The following command prints low level information that includes
+; offsets within the debug information section, debug location
+; operands, linkage names, etc.
+
+; RUN: llvm-debuginfo-analyzer --attribute=all \
+; RUN:                         --print=all \
+; RUN:                         %p/Inputs/test-codeview-clang.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s
+
+; ONE:      Logical View:
+; ONE-NEXT: [0x0000000000][000]            {File} '{{.*}}test-codeview-clang.o' -> COFF-x86-64
+; ONE-EMPTY:
+; ONE-NEXT: [0x0000000000][001]              {CompileUnit} 'test.cpp'
+; ONE-NEXT: [0x0000000000][002]                {Producer} 'clang version 15.0.0 {{.*}}'
+; ONE-NEXT:                                    {Directory} 'test.cpp'
+; ONE-NEXT:                                    {Directory} 'x:/tests/input'
+; ONE-NEXT:                                    {File} 'general'
+; ONE-NEXT:                                    {File} 'test.cpp'
+; ONE-NEXT:                                    {Public} 'foo' [0x0000000000:0x0000000046]
+; ONE-NEXT: [0x0000000000][002]                {TypeAlias} 'INTPTR' -> [0x0000001001]'* const int'
+; ONE-NEXT: [0x0000000030][002]                {BaseType} 'bool'
+; ONE-NEXT: [0x0000000000][002]                {Function} extern not_inlined 'foo' -> [0x0000000074]'int'
+; ONE-NEXT: [0x0000000000][003]                  {Range} Lines 2:9 [0x0000000000:0x0000000046]
+; ONE-NEXT: [0x0000000000][003]                  {Linkage}  0x1 '?foo@@YAHPEBHI_N at Z'
+; ONE-NEXT: [0x0000000000][003]                  {TypeAlias} 'INTEGER' -> [0x0000000074]'int'
+; ONE-NEXT: [0x0000000000][003]                  {Parameter} 'ParamBool' -> [0x0000000030]'bool'
+; ONE-NEXT: [0x0000000000][004]                    {Coverage} 70.00% (49/70)
+; ONE-NEXT: [0x0000000000][004]                    {Location} Lines 3:9 [0x0000000016:0x0000000047]
+; ONE-NEXT: [0x0000000000][005]                      {Entry} frame_pointer_rel 27
+; ONE-NEXT: [0x0000000000][003]                  {Parameter} 'ParamPtr' -> [0x0000001001]'* const int'
+; ONE-NEXT: [0x0000000000][004]                    {Coverage} 70.00% (49/70)
+; ONE-NEXT: [0x0000000000][004]                    {Location} Lines 3:9 [0x0000000016:0x0000000047]
+; ONE-NEXT: [0x0000000000][005]                      {Entry} frame_pointer_rel 8
+; ONE-NEXT: [0x0000000000][003]                  {Parameter} 'ParamUnsigned' -> [0x0000000075]'unsigned'
+; ONE-NEXT: [0x0000000000][004]                    {Coverage} 70.00% (49/70)
+; ONE-NEXT: [0x0000000000][004]                    {Location} Lines 3:9 [0x0000000016:0x0000000047]
+; ONE-NEXT: [0x0000000000][005]                      {Entry} frame_pointer_rel 20
+; ONE-NEXT: [0x0000000000][003]                  {Block}
+; ONE-NEXT: [0x0000000000][004]                    {Range} Lines ?:? [0x0000000021:0x0000000035]
+; ONE-NEXT: [0x0000000000][004]                    {Variable} 'CONSTANT' -> [0x0000001000]'const int'
+; ONE-NEXT: [0x0000000000][005]                      {Coverage} 105.00% (21/20)
+; ONE-NEXT: [0x0000000000][005]                      {Location} Lines ?:? [0x0000000021:0x0000000036]
+; ONE-NEXT: [0x0000000000][006]                        {Entry} frame_pointer_rel 4
+; ONE-EMPTY:
+; ONE-NEXT: [0x0000000021][004]   {Source} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x0000000021][004]      5             {Line} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x0000000021][004]                    {Code} 'movl	$0x7, 0x4(%rsp)'
+; ONE-NEXT: [0x0000000029][004]      6             {Line} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x0000000029][004]                    {Code} 'movl	$0x7, 0x1c(%rsp)'
+; ONE-NEXT: [0x0000000031][004]                    {Code} 'jmp	0x8'
+; ONE-NEXT: [0x0000000000][003]      2           {Line} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x0000000000][003]                  {Code} 'subq	$0x20, %rsp'
+; ONE-NEXT: [0x0000000004][003]                  {Code} 'andb	$0x1, %r8b'
+; ONE-NEXT: [0x0000000008][003]                  {Code} 'movb	%r8b, 0x1b(%rsp)'
+; ONE-NEXT: [0x000000000d][003]                  {Code} 'movl	%edx, 0x14(%rsp)'
+; ONE-NEXT: [0x0000000011][003]                  {Code} 'movq	%rcx, 0x8(%rsp)'
+; ONE-NEXT: [0x0000000016][003]      3           {Line} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x0000000016][003]                  {Code} 'testb	$0x1, 0x1b(%rsp)'
+; ONE-NEXT: [0x000000001b][003]                  {Code} 'je	0x15'
+; ONE-NEXT: [0x0000000036][003]      8           {Line} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x0000000036][003]                  {Code} 'movl	0x14(%rsp), %eax'
+; ONE-NEXT: [0x000000003a][003]                  {Code} 'movl	%eax, 0x1c(%rsp)'
+; ONE-NEXT: [0x000000003e][003]      9           {Line} 'x:/tests/input/general/test.cpp'
+; ONE-NEXT: [0x000000003e][003]                  {Code} 'movl	0x1c(%rsp), %eax'
+; ONE-NEXT: [0x0000000042][003]                  {Code} 'addq	$0x20, %rsp'
+; ONE-NEXT: [0x0000000046][003]                  {Code} 'retq'
+; ONE-NEXT: [0x0000000074][002]                {BaseType} 'int'
+; ONE-NEXT: [0x0000000075][002]                {BaseType} 'unsigned'
+; ONE-EMPTY:
+; ONE-NEXT: -----------------------------
+; ONE-NEXT: Element      Total    Printed
+; ONE-NEXT: -----------------------------
+; ONE-NEXT: Scopes           3          3
+; ONE-NEXT: Symbols          4          4
+; ONE-NEXT: Types            5          5
+; ONE-NEXT: Lines           21         21
+; ONE-NEXT: -----------------------------
+; ONE-NEXT: Total           33         33
+; ONE-EMPTY:
+; ONE-NEXT: Scope Sizes:
+; ONE-EMPTY:
+; ONE-NEXT: Totals by lexical level:
+; ONE-EMPTY:

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-clang.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-clang.o
new file mode 100644
index 0000000000000..9b27acbc8b105
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-clang.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-msvc.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-msvc.o
new file mode 100644
index 0000000000000..cc1751b6c29b8
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/hello-world-codeview-msvc.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-clang.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-clang.o
new file mode 100644
index 0000000000000..f6cfe38895d83
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-clang.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-msvc.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-msvc.o
new file mode 100644
index 0000000000000..84456b51e8a4e
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-43860-codeview-msvc.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-clang.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-clang.o
new file mode 100644
index 0000000000000..76aa35eff6e30
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-clang.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-msvc.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-msvc.o
new file mode 100644
index 0000000000000..d7ddb946cc2c0
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-44884-codeview-msvc.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-clang.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-clang.o
new file mode 100644
index 0000000000000..0c986283f3ea7
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-clang.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-msvc.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-msvc.o
new file mode 100644
index 0000000000000..cfc03974d35e0
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/pr-46466-codeview-msvc.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-clang.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-clang.o
new file mode 100644
index 0000000000000..279387d48b212
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-clang.o 
diff er

diff  --git a/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-msvc.o b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-msvc.o
new file mode 100644
index 0000000000000..70ec06270a0b9
Binary files /dev/null and b/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-msvc.o 
diff er

diff  --git a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt
index 82c44c20e9d30..1b93d77a20bd6 100644
--- a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt
+++ b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt
@@ -1,7 +1,7 @@
 set(LLVM_LINK_COMPONENTS
   AllTargetsDescs
-  AllTargetsInfos
   AllTargetsDisassemblers
+  AllTargetsInfos
   DebugInfoLogicalView
   MC
   MCDisassembler
@@ -9,6 +9,7 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_llvm_unittest_with_input_files(DebugInfoLogicalViewTests
+  CodeViewReaderTest.cpp
   CommandLineOptionsTest.cpp
   CompareElementsTest.cpp
   ELFReaderTest.cpp

diff  --git a/llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp b/llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp
new file mode 100644
index 0000000000000..eb9477e40ed90
--- /dev/null
+++ b/llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp
@@ -0,0 +1,495 @@
+//===- llvm/unittest/DebugInfo/LogicalView/CodeViewReaderTest.cpp ---------===//
+//
+// 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/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
+#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/COM.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Testing/Support/Error.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+extern const char *TestMainArgv0;
+
+namespace {
+
+const char *CodeViewClang = "test-codeview-clang.o";
+const char *CodeViewMsvc = "test-codeview-msvc.o";
+const char *CodeViewPdbMsvc = "test-codeview-pdb-msvc.o";
+
+// Helper function to get the first scope child from the given parent.
+LVScope *getFirstScopeChild(LVScope *Parent) {
+  EXPECT_NE(Parent, nullptr);
+  const LVScopes *Scopes = Parent->getScopes();
+  EXPECT_NE(Scopes, nullptr);
+  EXPECT_EQ(Scopes->size(), 1u);
+
+  LVScopes::const_iterator Iter = Scopes->begin();
+  LVScope *Child = *Iter;
+  EXPECT_NE(Child, nullptr);
+  return Child;
+}
+
+// Helper function to create a reader.
+std::unique_ptr<LVReader> createReader(LVReaderHandler &ReaderHandler,
+                                       SmallString<128> &InputsDir,
+                                       StringRef Filename) {
+  SmallString<128> ObjectName(InputsDir);
+  llvm::sys::path::append(ObjectName, Filename);
+
+  Expected<std::unique_ptr<LVReader>> ReaderOrErr =
+      ReaderHandler.createReader(std::string(ObjectName));
+  EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded());
+  std::unique_ptr<LVReader> Reader = std::move(*ReaderOrErr);
+  EXPECT_NE(Reader, nullptr);
+  return Reader;
+}
+
+// Check the logical elements basic properties (Clang - Codeview).
+void checkElementPropertiesClangCodeview(LVReader *Reader) {
+  LVScopeRoot *Root = Reader->getScopesRoot();
+  LVScopeCompileUnit *CompileUnit =
+      static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+  LVScopeFunction *Function =
+      static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit));
+
+  EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
+  EXPECT_EQ(Root->getName(), CodeViewClang);
+
+  EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
+  EXPECT_TRUE(CompileUnit->getProducer().startswith("clang"));
+  EXPECT_EQ(CompileUnit->getName(), "test.cpp");
+
+  EXPECT_EQ(Function->lineCount(), 16u);
+  EXPECT_EQ(Function->scopeCount(), 1u);
+  EXPECT_EQ(Function->symbolCount(), 3u);
+  EXPECT_EQ(Function->typeCount(), 1u);
+  EXPECT_EQ(Function->rangeCount(), 1u);
+
+  const LVLocations *Ranges = Function->getRanges();
+  ASSERT_NE(Ranges, nullptr);
+  ASSERT_EQ(Ranges->size(), 1u);
+  LVLocations::const_iterator IterLocation = Ranges->begin();
+  LVLocation *Location = (*IterLocation);
+  EXPECT_STREQ(Location->getIntervalInfo().c_str(),
+               "{Range} Lines 2:9 [0x0000000000:0x0000000046]");
+
+  LVRange RangeList;
+  Function->getRanges(RangeList);
+
+  const LVRangeEntries &RangeEntries = RangeList.getEntries();
+  ASSERT_EQ(RangeEntries.size(), 2u);
+  LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
+  LVRangeEntry RangeEntry = *IterRanges;
+  EXPECT_EQ(RangeEntry.lower(), 0u);
+  EXPECT_EQ(RangeEntry.upper(), 0x46u);
+  EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+  EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
+  EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+  ++IterRanges;
+  RangeEntry = *IterRanges;
+  EXPECT_EQ(RangeEntry.lower(), 0x21u);
+  EXPECT_EQ(RangeEntry.upper(), 0x35u);
+  EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+  EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
+  EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+  const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+  ASSERT_EQ(PublicNames.size(), 1u);
+  LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+  LVScope *Foo = (*IterNames).first;
+  EXPECT_EQ(Foo->getName(), "foo");
+  EXPECT_EQ(Foo->getLineNumber(), 0u);
+  LVNameInfo NameInfo = (*IterNames).second;
+  EXPECT_EQ(NameInfo.first, 0u);
+  EXPECT_EQ(NameInfo.second, 0x46u);
+
+  // Lines (debug and assembler) for 'foo'.
+  const LVLines *Lines = Foo->getLines();
+  ASSERT_NE(Lines, nullptr);
+  EXPECT_EQ(Lines->size(), 0x10u);
+}
+
+// Check the logical elements basic properties (MSVC - Codeview).
+void checkElementPropertiesMsvcCodeview(LVReader *Reader) {
+  LVScopeRoot *Root = Reader->getScopesRoot();
+  LVScopeCompileUnit *CompileUnit =
+      static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+  LVScopeFunction *Function =
+      static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit));
+
+  EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
+  EXPECT_EQ(Root->getName(), CodeViewMsvc);
+
+  EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
+  EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft"));
+  EXPECT_EQ(CompileUnit->getName(), "test.cpp");
+
+  EXPECT_EQ(Function->lineCount(), 14u);
+  EXPECT_EQ(Function->scopeCount(), 1u);
+  EXPECT_EQ(Function->symbolCount(), 3u);
+  EXPECT_EQ(Function->typeCount(), 0u);
+  EXPECT_EQ(Function->rangeCount(), 1u);
+
+  const LVLocations *Ranges = Function->getRanges();
+  ASSERT_NE(Ranges, nullptr);
+  ASSERT_EQ(Ranges->size(), 1u);
+  LVLocations::const_iterator IterLocation = Ranges->begin();
+  LVLocation *Location = (*IterLocation);
+  EXPECT_STREQ(Location->getIntervalInfo().c_str(),
+               "{Range} Lines 2:9 [0x0000000000:0x0000000031]");
+
+  LVRange RangeList;
+  Function->getRanges(RangeList);
+
+  const LVRangeEntries &RangeEntries = RangeList.getEntries();
+  ASSERT_EQ(RangeEntries.size(), 2u);
+  LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
+  LVRangeEntry RangeEntry = *IterRanges;
+  EXPECT_EQ(RangeEntry.lower(), 0u);
+  EXPECT_EQ(RangeEntry.upper(), 0x31u);
+  EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+  EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
+  EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+  ++IterRanges;
+  RangeEntry = *IterRanges;
+  EXPECT_EQ(RangeEntry.lower(), 0x1bu);
+  EXPECT_EQ(RangeEntry.upper(), 0x28u);
+  EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+  EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
+  EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+  const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+  ASSERT_EQ(PublicNames.size(), 1u);
+  LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+  LVScope *Foo = (*IterNames).first;
+  EXPECT_EQ(Foo->getName(), "foo");
+  EXPECT_EQ(Foo->getLineNumber(), 0u);
+  LVNameInfo NameInfo = (*IterNames).second;
+  EXPECT_EQ(NameInfo.first, 0u);
+  EXPECT_EQ(NameInfo.second, 0x31u);
+
+  // Lines (debug and assembler) for 'foo'.
+  const LVLines *Lines = Foo->getLines();
+  ASSERT_NE(Lines, nullptr);
+  EXPECT_EQ(Lines->size(), 0x0eu);
+}
+
+// Check the logical elements basic properties (MSVC - PDB).
+void checkElementPropertiesMsvcCodeviewPdb(LVReader *Reader) {
+  LVScopeRoot *Root = Reader->getScopesRoot();
+  LVScopeCompileUnit *CompileUnit =
+      static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+  LVScopeFunction *Function =
+      static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit));
+
+  EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
+  EXPECT_EQ(Root->getName(), CodeViewPdbMsvc);
+
+  EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
+  EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft"));
+  EXPECT_EQ(CompileUnit->getName(), "test.cpp");
+
+  EXPECT_EQ(Function->lineCount(), 14u);
+  EXPECT_EQ(Function->scopeCount(), 1u);
+  EXPECT_EQ(Function->symbolCount(), 3u);
+  EXPECT_EQ(Function->typeCount(), 0u);
+  EXPECT_EQ(Function->rangeCount(), 1u);
+
+  const LVLocations *Ranges = Function->getRanges();
+  ASSERT_NE(Ranges, nullptr);
+  ASSERT_EQ(Ranges->size(), 1u);
+  LVLocations::const_iterator IterLocation = Ranges->begin();
+  LVLocation *Location = (*IterLocation);
+  EXPECT_STREQ(Location->getIntervalInfo().c_str(),
+               "{Range} Lines 2:9 [0x0000000000:0x0000000031]");
+
+  LVRange RangeList;
+  Function->getRanges(RangeList);
+
+  const LVRangeEntries &RangeEntries = RangeList.getEntries();
+  ASSERT_EQ(RangeEntries.size(), 2u);
+  LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
+  LVRangeEntry RangeEntry = *IterRanges;
+  EXPECT_EQ(RangeEntry.lower(), 0u);
+  EXPECT_EQ(RangeEntry.upper(), 0x31u);
+  EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+  EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
+  EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+  ++IterRanges;
+  RangeEntry = *IterRanges;
+  EXPECT_EQ(RangeEntry.lower(), 0x1bu);
+  EXPECT_EQ(RangeEntry.upper(), 0x28u);
+  EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+  EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
+  EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+  const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+  ASSERT_EQ(PublicNames.size(), 1u);
+  LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+  LVScope *Foo = (*IterNames).first;
+  EXPECT_EQ(Foo->getName(), "foo");
+  EXPECT_EQ(Foo->getLineNumber(), 0u);
+  LVNameInfo NameInfo = (*IterNames).second;
+  EXPECT_EQ(NameInfo.first, 0u);
+  EXPECT_EQ(NameInfo.second, 0x31u);
+
+  // Lines (debug and assembler) for 'foo'.
+  const LVLines *Lines = Foo->getLines();
+  ASSERT_NE(Lines, nullptr);
+  EXPECT_EQ(Lines->size(), 0x0eu);
+}
+
+struct SelectionInfo {
+  const char *Name;
+  LVElementGetFunction Function;
+};
+
+// Check the logical elements selection.
+void checkElementSelection(LVReader *Reader, std::vector<SelectionInfo> &Data,
+                           size_t Size) {
+  LVScopeRoot *Root = Reader->getScopesRoot();
+  LVScopeCompileUnit *CompileUnit =
+      static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+
+  // Get the matched elements.
+  LVElements MatchedElements = CompileUnit->getMatchedElements();
+  std::map<StringRef, LVElement *> MapElements;
+  for (LVElement *Element : MatchedElements)
+    MapElements[Element->getName()] = Element;
+  ASSERT_EQ(MapElements.size(), Size);
+
+  std::map<StringRef, LVElement *>::iterator Iter = MapElements.begin();
+  for (const SelectionInfo &Entry : Data) {
+    // Get matched element.
+    EXPECT_NE(Iter, MapElements.end());
+    LVElement *Element = Iter->second;
+    ASSERT_NE(Element, nullptr);
+    EXPECT_NE(Element->getName().find(Entry.Name), StringRef::npos);
+    EXPECT_EQ((Element->*Entry.Function)(), 1u);
+    ++Iter;
+  }
+
+  // Get the parents for the matched elements.
+  LVScopes MatchedScopes = CompileUnit->getMatchedScopes();
+  std::set<StringRef> SetScopes;
+  for (LVScope *Scope : MatchedScopes)
+    SetScopes.insert(Scope->getName());
+  ASSERT_EQ(SetScopes.size(), 3u);
+
+  // Parents of selected elements.
+  std::set<StringRef>::iterator IterScope;
+  IterScope = SetScopes.find("foo");
+  EXPECT_NE(IterScope, SetScopes.end());
+  IterScope = SetScopes.find("foo::?");
+  EXPECT_NE(IterScope, SetScopes.end());
+  IterScope = SetScopes.find("test.cpp");
+  EXPECT_NE(IterScope, SetScopes.end());
+}
+
+// Check the logical elements comparison.
+void checkElementComparison(LVReader *Reference, LVReader *Target) {
+  LVCompare Compare(nulls());
+  Error Err = Compare.execute(Reference, Target);
+  ASSERT_THAT_ERROR(std::move(Err), Succeeded());
+
+  // Get comparison table.
+  LVPassTable PassTable = Compare.getPassTable();
+  ASSERT_EQ(PassTable.size(), 2u);
+
+  LVReader *Reader;
+  LVElement *Element;
+  LVComparePass Pass;
+
+  // Reference: Missing TypeDefinition 'INTEGER'
+  std::tie(Reader, Element, Pass) = PassTable[0];
+  ASSERT_NE(Reader, nullptr);
+  ASSERT_NE(Element, nullptr);
+  EXPECT_EQ(Reader, Reference);
+  EXPECT_EQ(Element->getLevel(), 3u);
+  EXPECT_EQ(Element->getLineNumber(), 0u);
+  EXPECT_EQ(Element->getName(), "INTEGER");
+  EXPECT_EQ(Pass, LVComparePass::Missing);
+
+  // Target: Added TypeDefinition 'INTEGER'
+  std::tie(Reader, Element, Pass) = PassTable[1];
+  ASSERT_NE(Reader, nullptr);
+  ASSERT_NE(Element, nullptr);
+  EXPECT_EQ(Reader, Target);
+  EXPECT_EQ(Element->getLevel(), 4u);
+  EXPECT_EQ(Element->getLineNumber(), 0u);
+  EXPECT_EQ(Element->getName(), "INTEGER");
+  EXPECT_EQ(Pass, LVComparePass::Added);
+}
+
+// Logical elements properties.
+void elementProperties(SmallString<128> &InputsDir) {
+  // Reader options.
+  LVOptions ReaderOptions;
+  ReaderOptions.setAttributeOffset();
+  ReaderOptions.setAttributeFormat();
+  ReaderOptions.setAttributeFilename();
+  ReaderOptions.setAttributeProducer();
+  ReaderOptions.setAttributePublics();
+  ReaderOptions.setAttributeRange();
+  ReaderOptions.setAttributeLocation();
+  ReaderOptions.setPrintAll();
+  ReaderOptions.resolveDependencies();
+
+  std::vector<std::string> Objects;
+  ScopedPrinter W(outs());
+  LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
+
+  // Check logical elements properties.
+  {
+    std::unique_ptr<LVReader> Reader =
+        createReader(ReaderHandler, InputsDir, CodeViewClang);
+    checkElementPropertiesClangCodeview(Reader.get());
+  }
+  {
+    std::unique_ptr<LVReader> Reader =
+        createReader(ReaderHandler, InputsDir, CodeViewMsvc);
+    checkElementPropertiesMsvcCodeview(Reader.get());
+  }
+  {
+    std::unique_ptr<LVReader> Reader =
+        createReader(ReaderHandler, InputsDir, CodeViewPdbMsvc);
+    checkElementPropertiesMsvcCodeviewPdb(Reader.get());
+  }
+}
+
+// Logical elements selection.
+void elementSelection(SmallString<128> &InputsDir) {
+  // Reader options.
+  LVOptions ReaderOptions;
+  ReaderOptions.setAttributeOffset();
+  ReaderOptions.setPrintAll();
+
+  ReaderOptions.setSelectIgnoreCase();
+  ReaderOptions.setSelectUseRegex();
+
+  ReaderOptions.setReportList(); // Matched elements.
+  ReaderOptions.setReportView(); // Parents for matched elements.
+
+  // Add patterns.
+  ReaderOptions.Select.Generic.insert("foo");
+  ReaderOptions.Select.Generic.insert("movl[ \t]?%");
+  ReaderOptions.Select.Generic.insert("INT[a-z]*");
+  ReaderOptions.Select.Generic.insert("CONSTANT");
+
+  ReaderOptions.resolveDependencies();
+
+  std::vector<std::string> Objects;
+  ScopedPrinter W(outs());
+  LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
+
+  // Check logical elements selection.
+  {
+    std::vector<SelectionInfo> DataClang = {
+        {"* const int", &LVElement::getIsType},
+        {"CONSTANT", &LVElement::getIsSymbol},
+        {"INTEGER", &LVElement::getIsType},
+        {"INTPTR", &LVElement::getIsType},
+        {"ParamPtr", &LVElement::getIsSymbol},
+        {"const int", &LVElement::getIsType},
+        {"foo", &LVElement::getIsScope},
+        {"foo::?", &LVElement::getIsScope},
+        {"int", &LVElement::getIsType},
+        {"movl", &LVElement::getIsLine},
+        {"movl", &LVElement::getIsLine}};
+    std::unique_ptr<LVReader> Reader =
+        createReader(ReaderHandler, InputsDir, CodeViewClang);
+    checkElementSelection(Reader.get(), DataClang, DataClang.size());
+  }
+  {
+    std::vector<SelectionInfo> DataMsvc = {
+        {"* const int", &LVElement::getIsType},
+        {"CONSTANT", &LVElement::getIsSymbol},
+        {"INTEGER", &LVElement::getIsType},
+        {"INTPTR", &LVElement::getIsType},
+        {"ParamPtr", &LVElement::getIsSymbol},
+        {"const int", &LVElement::getIsType},
+        {"foo", &LVElement::getIsScope},
+        {"foo::?", &LVElement::getIsScope},
+        {"int", &LVElement::getIsType},
+        {"movl", &LVElement::getIsLine}};
+    std::unique_ptr<LVReader> Reader =
+        createReader(ReaderHandler, InputsDir, CodeViewMsvc);
+    checkElementSelection(Reader.get(), DataMsvc, DataMsvc.size());
+  }
+}
+
+// Compare logical elements.
+void compareElements(SmallString<128> &InputsDir) {
+  // Reader options.
+  LVOptions ReaderOptions;
+  ReaderOptions.setAttributeOffset();
+  ReaderOptions.setPrintLines();
+  ReaderOptions.setPrintSymbols();
+  ReaderOptions.setPrintTypes();
+  ReaderOptions.setCompareLines();
+  ReaderOptions.setCompareSymbols();
+  ReaderOptions.setCompareTypes();
+
+  ReaderOptions.resolveDependencies();
+
+  std::vector<std::string> Objects;
+  ScopedPrinter W(outs());
+  LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
+
+  // Check logical comparison.
+  std::unique_ptr<LVReader> Reference =
+      createReader(ReaderHandler, InputsDir, CodeViewClang);
+  std::unique_ptr<LVReader> Target =
+      createReader(ReaderHandler, InputsDir, CodeViewMsvc);
+  checkElementComparison(Reference.get(), Target.get());
+}
+
+TEST(LogicalViewTest, CodeViewReader) {
+  // Initialize targets and assembly printers/parsers.
+  llvm::InitializeAllTargetInfos();
+  llvm::InitializeAllTargetMCs();
+  InitializeAllDisassemblers();
+
+  llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
+
+  // This test requires a x86-registered-target.
+  Triple TT;
+  TT.setArch(Triple::x86_64);
+  TT.setVendor(Triple::UnknownVendor);
+  TT.setOS(Triple::UnknownOS);
+
+  std::string TargetLookupError;
+  if (!TargetRegistry::lookupTarget(std::string(TT.str()), TargetLookupError))
+    return;
+
+  SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);
+
+  // Logical elements general properties and selection.
+  elementProperties(InputsDir);
+  elementSelection(InputsDir);
+
+  // Compare logical elements.
+  compareElements(InputsDir);
+}
+
+} // namespace

diff  --git a/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-clang.o b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-clang.o
new file mode 100644
index 0000000000000..571f23ee3293f
Binary files /dev/null and b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-clang.o 
diff er

diff  --git a/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-msvc.o b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-msvc.o
new file mode 100644
index 0000000000000..03bd29b3b9fc7
Binary files /dev/null and b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-msvc.o 
diff er

diff  --git a/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc-.pdb b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc-.pdb
new file mode 100644
index 0000000000000..dc576e22e1e40
Binary files /dev/null and b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc-.pdb 
diff er

diff  --git a/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc.o b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc.o
new file mode 100644
index 0000000000000..33c2cbf241a07
Binary files /dev/null and b/llvm/unittests/DebugInfo/LogicalView/Inputs/test-codeview-pdb-msvc.o 
diff er


        


More information about the llvm-commits mailing list