StephenTozer created this revision.
StephenTozer added a project: debug-info.
Herald added subscribers: ormris, hiraditya.
Herald added a project: All.
StephenTozer requested review of this revision.
Herald added subscribers: llvm-commits, lldb-commits, cfe-commits, jplehr, 
sstefan1, MaskRay.
Herald added a reviewer: jdoerfert.
Herald added projects: clang, LLDB, LLVM.

NOTE: This is not intended to be merged into LLVM, as the implementation is 
incomplete and hacked-in and **is not being actively developed**; the intention 
is simply to provide a basic working implementation that can be analyzed and 
potentially expanded on by any interested parties. For more information see 
this discourse post.

This patch implements the Two-Level Line Tables (TLLT) proposal 
<https://dwarfstd.org/issues/140906.1.html> currently aiming for Dwarf 6. This 
proposal allows the line table, which is DWARF's data structure used to map 
instructions to source locations and vice versa, to be emitted as a pair of 
tables instead of one, one of which maps source locations to instructions (the 
Logicals table) and the other of which maps instructions to source locations 
(the Actuals table). The proposal then extends the Logicals table by adding a 
context column, allowing entries in that table that represent inlined 
instructions to refer to another Logicals entry as being the inlined call 
location for that instruction, creating records for an inlined call stack in 
the line table and allowing the inlined callsites to be mapped to an 
instruction in the debugger.

This solves one of the current weaknesses in DWARF debugging, which is the 
inability to set breakpoints or step onto inlined callsites. However, there are 
other proposals (such as Location View Numbering 
<https://dwarfstd.org/issues/170427.1.html>) that could achieve the same thing, 
and the costs of TLLT in storage size and design complexity may make it a 
non-ideal solution to this problem. We currently have no plans to develop this 
implementation further, but this small demo may be useful to other developers 
who have an interest in pursuing this, and it is still possible that a good 
case may be made for TLLT as the best solution to the problem of representing 
inlined callsites.

As mentioned in the header, this patch was the result of aiming to get a basic 
functioning implementation in a very short time; it is not intended to be 
merged and is not of suitable quality, particularly in LLDB (with which we have 
no familiarity).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D152708

Files:
  clang/include/clang/Basic/CodeGenOptions.def
  clang/include/clang/Driver/Options.td
  clang/lib/CodeGen/BackendUtil.cpp
  clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
  clang/lib/Driver/ToolChains/Clang.cpp
  lldb/include/lldb/Symbol/LineTable.h
  lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
  lldb/source/Symbol/LineTable.cpp
  llvm/include/llvm/BinaryFormat/Dwarf.def
  llvm/include/llvm/CodeGen/CommandFlags.h
  llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
  llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
  llvm/include/llvm/MC/MCContext.h
  llvm/include/llvm/MC/MCDwarf.h
  llvm/include/llvm/MC/MCStreamer.h
  llvm/include/llvm/Target/TargetOptions.h
  llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
  llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
  llvm/lib/CodeGen/CommandFlags.cpp
  llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
  llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
  llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
  llvm/lib/MC/MCContext.cpp
  llvm/lib/MC/MCDwarf.cpp
  llvm/lib/MC/MCStreamer.cpp

Index: llvm/lib/MC/MCStreamer.cpp
===================================================================
--- llvm/lib/MC/MCStreamer.cpp
+++ llvm/lib/MC/MCStreamer.cpp
@@ -268,6 +268,18 @@
                                   Discriminator);
 }
 
+void MCStreamer::emitDwarfLogicalLocDirective(
+    unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags,
+    unsigned Isa, unsigned Discriminator, StringRef FileName, unsigned Context,
+    StringRef FunctionName) {
+  getContext().addCurrentDwarfLogicalLoc(FileNo, Line, Column, Flags, Isa,
+                                         Discriminator, Context, FunctionName);
+}
+
+void MCStreamer::emitDwarfActualLocDirective(unsigned Line, unsigned Flags) {
+  getContext().setCurrentDwarfActualLoc(Line, Flags);
+}
+
 MCSymbol *MCStreamer::getDwarfLineTableSymbol(unsigned CUID) {
   MCDwarfLineTable &Table = getContext().getMCDwarfLineTable(CUID);
   if (!Table.getLabel()) {
Index: llvm/lib/MC/MCDwarf.cpp
===================================================================
--- llvm/lib/MC/MCDwarf.cpp
+++ llvm/lib/MC/MCDwarf.cpp
@@ -92,28 +92,69 @@
 // a line entry made for it is made.
 //
 void MCDwarfLineEntry::make(MCStreamer *MCOS, MCSection *Section) {
-  if (!MCOS->getContext().getDwarfLocSeen())
-    return;
+  uint16_t VersionNumber = MCOS->getContext().getDwarfVersion();
+  // If we're emitting TLLT, it should be sufficient to check DwarfActualLocSeen
+  // only, as we will always emit 1 Actual entry if there is anything to emit,
+  // while Logical entries are 0..many.
+  bool EnableTwoLevelLineTables =
+      MCOS->getContext().getEnableTwoLevelLineTables();
+  if (VersionNumber < 5 || !EnableTwoLevelLineTables) {
+    if (!MCOS->getContext().getDwarfLocSeen())
+      return;
+  } else {
+    if (!MCOS->getContext().getDwarfActualLocSeen())
+      return;
+  }
 
   // Create a symbol at in the current section for use in the line entry.
   MCSymbol *LineSym = MCOS->getContext().createTempSymbol();
   // Set the value of the symbol to use for the MCDwarfLineEntry.
   MCOS->emitLabel(LineSym);
 
-  // Get the current .loc info saved in the context.
-  const MCDwarfLoc &DwarfLoc = MCOS->getContext().getCurrentDwarfLoc();
+  // Emit SLLT.
+  if (VersionNumber < 5 || !EnableTwoLevelLineTables) {
+    // Get the current .loc info saved in the context.
+    const MCDwarfLoc &DwarfLoc = MCOS->getContext().getCurrentDwarfLoc();
 
-  // Create a (local) line entry with the symbol and the current .loc info.
-  MCDwarfLineEntry LineEntry(LineSym, DwarfLoc);
+    // Create a (local) line entry with the symbol and the current .loc info.
+    MCDwarfLineEntry LineEntry(LineSym, DwarfLoc);
 
-  // clear DwarfLocSeen saying the current .loc info is now used.
-  MCOS->getContext().clearDwarfLocSeen();
+    // clear DwarfLocSeen saying the current .loc info is now used.
+    MCOS->getContext().clearDwarfLocSeen();
 
-  // Add the line entry to this section's entries.
-  MCOS->getContext()
+    // Add the line entry to this section's entries.
+    MCOS->getContext()
       .getMCDwarfLineTable(MCOS->getContext().getDwarfCompileUnitID())
       .getMCLineSections()
       .addLineEntry(LineEntry, Section);
+    return;
+  }
+
+  //// Emit TLLT.
+  // Emit Logicals.
+  for (const MCDwarfLoc &DwarfLogicalLoc : MCOS->getContext().getCurrentDwarfLogicalsLoc()) {
+    // Create a logical line entry with the symbol and the current .loc info.
+    MCDwarfLineEntry LineEntry(LineSym, DwarfLogicalLoc);
+    // Add the line entry to this section's entries.
+    MCOS->getContext()
+      .getMCDwarfLineTable(MCOS->getContext().getDwarfCompileUnitID())
+      .getMCLineSections()
+      .addLogicalLineEntry(LineEntry, Section);
+  }
+  MCOS->getContext().clearDwarfLogicalLocsSeen();
+
+  // Emit Actual.
+  // Get the current .loc info saved in the context.
+  const MCDwarfLoc &DwarfActualLoc = MCOS->getContext().getCurrentDwarfActualLoc();
+  // Create a (local) line entry with the symbol and the current .loc info.
+  MCDwarfLineEntry LineEntry(LineSym, DwarfActualLoc);
+  // clear DwarfLocSeen saying the current .loc info is now used.
+  MCOS->getContext().clearDwarfActualLocSeen();
+  // Add the line entry to this section's entries.
+  MCOS->getContext()
+    .getMCDwarfLineTable(MCOS->getContext().getDwarfCompileUnitID())
+    .getMCLineSections()
+    .addActualLineEntry(LineEntry, Section);
 }
 
 //
@@ -168,9 +209,12 @@
 //
 void MCDwarfLineTable::emitOne(
     MCStreamer *MCOS, MCSection *Section,
-    const MCLineSection::MCDwarfLineEntryCollection &LineEntries) {
+    const MCLineSection::MCDwarfLineEntryCollection &LineEntries,
+    std::optional<MCDwarfLineStr> &LineStr) {
 
-  unsigned FileNum, LastLine, Column, Flags, Isa, Discriminator;
+  // TLLT FIXME: FunctionName might need to be size_t, not clear why unsigned
+  // is used for other types currently.
+  unsigned FileNum, LastLine, Column, Flags, Isa, Discriminator, Context, FunctionName;
   MCSymbol *LastLabel;
   auto init = [&]() {
     FileNum = 1;
@@ -180,6 +224,8 @@
     Isa = 0;
     Discriminator = 0;
     LastLabel = nullptr;
+    Context = 0;
+    FunctionName = 0;
   };
   init();
 
@@ -232,6 +278,15 @@
       MCOS->emitInt8(dwarf::DW_LNS_set_prologue_end);
     if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN)
       MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin);
+    // Special case: We always update subprogram and function name at the same
+    // time.
+    if (Context != LineEntry.getContext()) {
+      Context = LineEntry.getContext();
+      FunctionName = Context == 0 ? 0 : LineStr->getRef(MCOS, LineEntry.getFunctionName());
+      MCOS->emitInt8(dwarf::DW_LNS_inlined_call);
+      MCOS->emitULEB128IntValue(Context);
+      MCOS->emitULEB128IntValue(FunctionName);
+    }
 
     // At this point we want to emit/create the sequence to encode the delta in
     // line numbers and the increment of the address from the previous Label
@@ -289,10 +344,10 @@
     return;
   std::optional<MCDwarfLineStr> NoLineStr(std::nullopt);
   MCOS.switchSection(Section);
-  MCOS.emitLabel(Header.Emit(&MCOS, Params, std::nullopt, NoLineStr).second);
+  MCOS.emitLabel(std::get<1>(Header.Emit(&MCOS, Params, std::nullopt, NoLineStr)));
 }
 
-std::pair<MCSymbol *, MCSymbol *>
+std::tuple<MCSymbol *, MCSymbol *, MCSymbol *>
 MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
                              std::optional<MCDwarfLineStr> &LineStr) const {
   static const char StandardOpcodeLengths[] = {
@@ -307,7 +362,8 @@
       1, // length of DW_LNS_fixed_advance_pc
       0, // length of DW_LNS_set_prologue_end
       0, // length of DW_LNS_set_epilogue_begin
-      1  // DW_LNS_set_isa
+      1, // DW_LNS_set_isa
+      2  // length of DW_LNS_inlined_call
   };
   assert(std::size(StandardOpcodeLengths) >=
          (Params.DWARF2LineOpcodeBase - 1U));
@@ -360,6 +416,11 @@
   } else
     MCOS->emitIntValue(Offset, RefSize);
 }
+size_t MCDwarfLineStr::getRef(MCStreamer *MCOS, StringRef Path) {
+  int RefSize =
+      dwarf::getDwarfOffsetByteSize(MCOS->getContext().getDwarfFormat());
+  return LineStrings.add(Path);
+}
 
 void MCDwarfLineTableHeader::emitV2FileDirTables(MCStreamer *MCOS) const {
   // First the directory table.
@@ -480,7 +541,7 @@
     emitOneV5FileEntry(MCOS, MCDwarfFiles[i], HasAllMD5, HasSource, LineStr);
 }
 
-std::pair<MCSymbol *, MCSymbol *>
+std::tuple<MCSymbol *, MCSymbol *, MCSymbol *>
 MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
                              ArrayRef<char> StandardOpcodeLengths,
                              std::optional<MCDwarfLineStr> &LineStr) const {
@@ -500,6 +561,10 @@
 
   // Next 2 bytes is the Version.
   unsigned LineTableVersion = context.getDwarfVersion();
+  // TLLT Note: Version "6" is acting something like a magic number here to
+  // indicate the TLLT extension is being used.
+  if (LineTableVersion >= 5 && context.getEnableTwoLevelLineTables())
+    LineTableVersion = 6;
   MCOS->emitInt16(LineTableVersion);
 
   // In v5, we get address info next.
@@ -518,6 +583,18 @@
 
   MCOS->emitLabel(ProStartSym);
 
+  MCSymbol *ActualsStartSym = ProEndSym;
+  bool EnableTwoLevelLineTables =
+      MCOS->getContext().getEnableTwoLevelLineTables();
+  if (LineTableVersion >= 5 && EnableTwoLevelLineTables) {
+    // Skip this assignment if we are emitting a SLLT in Dwarf 6 (i.e. TLLT are
+    // supported but not being used).
+    ActualsStartSym = context.createTempSymbol("actuals_start");
+    // Distance from prologue_end to the start of the Actuals table.
+    MCOS->emitAbsoluteSymbolDiff(ActualsStartSym, ProEndSym, OffsetSize);
+    // Form of strings references in the FunctionName line register.
+    MCOS->emitInt8((uint8_t)dwarf::DW_FORM_line_strp);
+  }
   // Parameters of the state machine, are next.
   MCOS->emitInt8(context.getAsmInfo()->getMinInstAlignment());
   // maximum_operations_per_instruction
@@ -545,16 +622,37 @@
   // end of the prologue (that was used in a previous expression).
   MCOS->emitLabel(ProEndSym);
 
-  return std::make_pair(LineStartSym, LineEndSym);
+  return std::make_tuple(LineStartSym, LineEndSym, ActualsStartSym);
 }
 
 void MCDwarfLineTable::emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params,
                               std::optional<MCDwarfLineStr> &LineStr) const {
-  MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second;
+  auto RecordedSymbols = Header.Emit(MCOS, Params, LineStr);
+  MCSymbol *LineEndSym = std::get<1>(RecordedSymbols);
+  MCSymbol *ActualsStartSym = std::get<2>(RecordedSymbols);
 
   // Put out the line tables.
-  for (const auto &LineSec : MCLineSections.getMCLineEntries())
-    emitOne(MCOS, LineSec.first, LineSec.second);
+  MCContext &context = MCOS->getContext();
+  bool UseTLLT =
+      context.getDwarfVersion() >= 5 && context.getEnableTwoLevelLineTables();
+  bool EmittedAlready = false;
+  if (!UseTLLT) {
+    for (const auto &LineSec : MCLineSections.getMCLineEntries()) {
+      assert(!EmittedAlready && "Only one set of line entries should be emitted!");
+      emitOne(MCOS, LineSec.first, LineSec.second, LineStr);
+      EmittedAlready = true;
+    }
+  } else {
+    for (const auto &LineSec : MCLineSections.getMCLogicalLineEntries()) {
+      assert(!EmittedAlready && "Only one set of line entries should be emitted!");
+      emitOne(MCOS, LineSec.first, LineSec.second, LineStr);
+      EmittedAlready = true;
+    }
+    MCOS->emitLabel(ActualsStartSym);
+    // Emit Actuals Table here.
+    for (const auto &LineSec : MCLineSections.getMCActualLineEntries())
+      emitOne(MCOS, LineSec.first, LineSec.second, LineStr);
+  }
 
   // This is the end of the section, so set the value of the symbol at the end
   // of this section (that was used in a previous expression).
Index: llvm/lib/MC/MCContext.cpp
===================================================================
--- llvm/lib/MC/MCContext.cpp
+++ llvm/lib/MC/MCContext.cpp
@@ -73,6 +73,7 @@
       MAI(mai), MRI(mri), MSTI(msti), Symbols(Allocator), UsedNames(Allocator),
       InlineAsmUsedLabelNames(Allocator),
       CurrentDwarfLoc(0, 0, 0, DWARF2_FLAG_IS_STMT, 0, 0),
+      CurrentDwarfActualLoc(0, 0, 0, DWARF2_FLAG_IS_STMT, 0, 0),
       AutoReset(DoAutoReset), TargetOptions(TargetOpts) {
   SecureLogFile = TargetOptions ? TargetOptions->AsSecureLogFile : "";
 
Index: llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
===================================================================
--- llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -909,7 +909,9 @@
     // Verify rows.
     uint64_t PrevAddress = 0;
     uint32_t RowIndex = 0;
-    for (const auto &Row : LineTable->Rows) {
+    // FIXME TLLT: We would eventually verify the TLLT rows as well, with
+    // slightly different validation rules.
+    for (const auto &Row : LineTable->SLLTRows) {
       // Verify row address.
       if (Row.Address.Address < PrevAddress) {
         ++NumDebugLineErrors;
@@ -921,7 +923,7 @@
 
         DWARFDebugLine::Row::dumpTableHeader(OS, 0);
         if (RowIndex > 0)
-          LineTable->Rows[RowIndex - 1].dump(OS);
+          LineTable->SLLTRows[RowIndex - 1].dump(OS);
         Row.dump(OS);
         OS << '\n';
       }
Index: llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
===================================================================
--- llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
+++ llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
@@ -42,7 +42,7 @@
 } // end anonymous namespace
 
 static bool versionIsSupported(uint16_t Version) {
-  return Version >= 2 && Version <= 5;
+  return Version >= 2 && Version <= 6;
 }
 
 void DWARFDebugLine::ContentTypeTracker::trackContentType(
@@ -103,10 +103,11 @@
 }
 
 void DWARFDebugLine::Prologue::clear() {
-  TotalLength = PrologueLength = 0;
+  TotalLength = PrologueLength = LogicalTableLength = 0;
   SegSelectorSize = 0;
   MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0;
   OpcodeBase = 0;
+  FunctionNameForm = dwarf::Form::DW_FORM_line_strp;
   FormParams = dwarf::FormParams({0, 0, DWARF32});
   ContentTypes = ContentTypeTracker();
   StandardOpcodeLengths.clear();
@@ -130,8 +131,16 @@
     OS << format("    address_size: %u\n", getAddressSize())
        << format(" seg_select_size: %u\n", SegSelectorSize);
   OS << format(" prologue_length: 0x%0*" PRIx64 "\n", OffsetDumpWidth,
-               PrologueLength)
-     << format(" min_inst_length: %u\n", MinInstLength)
+               PrologueLength);
+  // TLLT Note: The proposal isn't actually confirmed for version 6 yet, but we
+  // are using it to indicate the TLLT extension is in effect, though a SLLT
+  // may still be present.
+  if (getVersion() == 6)
+    OS << format("  actuals_offset: 0x%0*" PRIx64 "\n", OffsetDumpWidth,
+                 LogicalTableLength)
+       << "    fn_name_form: " << dwarf::FormEncodingString(FunctionNameForm)
+       << "\n";
+  OS << format(" min_inst_length: %u\n", MinInstLength)
      << format(getVersion() >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst)
      << format(" default_is_stmt: %u\n", DefaultIsStmt)
      << format("       line_base: %i\n", LineBase)
@@ -392,7 +401,17 @@
 
   PrologueLength =
       DebugLineData.getRelocatedValue(Cursor, sizeofPrologueLength());
+
   const uint64_t EndPrologueOffset = PrologueLength + Cursor.tell();
+
+  // TLLT Note: Version 6 is being used to indicate TLLT proposal is in effect
+  // (whether or not TLLT are being emitted here).
+  if (getVersion() == 6) {
+    LogicalTableLength =
+        DebugLineData.getRelocatedValue(Cursor, sizeofPrologueLength());
+    FunctionNameForm = dwarf::Form(DebugLineData.getU8(Cursor));
+  }
+
   DebugLineData = DWARFDataExtractor(DebugLineData, EndPrologueOffset);
   MinInstLength = DebugLineData.getU8(Cursor);
   if (getVersion() >= 4)
@@ -471,6 +490,9 @@
 void DWARFDebugLine::Row::reset(bool DefaultIsStmt) {
   Address.Address = 0;
   Address.SectionIndex = object::SectionedAddress::UndefSection;
+  Context = 0;
+  LogicalIndex = 0;
+  SubprogramOffset = DWARFFormValue();
   Line = 1;
   Column = 0;
   File = 1;
@@ -491,6 +513,23 @@
          "-------------\n";
 }
 
+void DWARFDebugLine::Row::dumpLogicalTableHeader(raw_ostream &OS,
+                                                 unsigned Indent) {
+  OS.indent(Indent) << "Logicals Table:\n";
+  OS.indent(Indent)
+      << "Row    Address            Line   Column File   Discriminator "
+         "Context Flags                Subprogram         \n";
+  OS.indent(Indent)
+      << "------ ------------------ ------ ------ ------ ------------- "
+         "------- -------------------- ------------------\n";
+}
+void DWARFDebugLine::Row::dumpActualTableHeader(raw_ostream &OS,
+                                                unsigned Indent) {
+  OS.indent(Indent) << "Actuals Table:\n";
+  OS.indent(Indent) << "Address            Logical Row ISA Flags\n";
+  OS.indent(Indent) << "------------------ ----------- --- -------------\n";
+}
+
 void DWARFDebugLine::Row::dump(raw_ostream &OS) const {
   OS << format("0x%16.16" PRIx64 " %6u %6u", Address.Address, Line, Column)
      << format(" %6u %3u %13u ", File, Isa, Discriminator)
@@ -499,6 +538,32 @@
      << (EpilogueBegin ? " epilogue_begin" : "")
      << (EndSequence ? " end_sequence" : "") << '\n';
 }
+void DWARFDebugLine::Row::dumpLogical(raw_ostream &OS) const {
+  OS << format("%6u 0x%16.16" PRIx64, LogicalIndex, Address.Address)
+     << format(" %6u %6u %6u %13u", Line, Column, File, Discriminator)
+     << format(" %7u", Context);
+  OS << (IsStmt ? " is_stmt" : "") << (PrologueEnd ? " prologue_end" : "")
+    << (EpilogueBegin ? " epilogue_begin" : "");
+  unsigned TrailingSpaces = 20;
+  if (IsStmt)
+    TrailingSpaces -= 8;
+  if (PrologueEnd)
+    TrailingSpaces -= 12;
+  for (unsigned idx = 0; idx < TrailingSpaces; ++idx)
+    OS << " ";
+  if (SubprogramOffset.getForm() != 0) {
+    OS << "  "; SubprogramOffset.dump(OS);
+  }
+  else {
+    OS << "                   ";
+  }
+  OS << "\n";
+}
+void DWARFDebugLine::Row::dumpActual(raw_ostream &OS) const {
+  OS << format("0x%16.16" PRIx64 " %11u %3u", Address.Address, Line, Isa)
+     << (BasicBlock ? " basic_block" : "")
+     << (EndSequence ? " end_sequence" : "") << '\n';
+}
 
 DWARFDebugLine::Sequence::Sequence() { reset(); }
 
@@ -517,13 +582,27 @@
                                      DIDumpOptions DumpOptions) const {
   Prologue.dump(OS, DumpOptions);
 
-  if (!Rows.empty()) {
+  if (!SLLTRows.empty()) {
     OS << '\n';
     Row::dumpTableHeader(OS, 0);
-    for (const Row &R : Rows) {
+    for (const Row &R : SLLTRows) {
       R.dump(OS);
     }
   }
+  if (!LogicalRows.empty()) {
+    OS << '\n';
+    Row::dumpLogicalTableHeader(OS, 0);
+    for (const Row &R : LogicalRows) {
+      R.dumpLogical(OS);
+    }
+  }
+  if (!ActualRows.empty()) {
+    OS << '\n';
+    Row::dumpActualTableHeader(OS, 0);
+    for (const Row &R : ActualRows) {
+      R.dumpActual(OS);
+    }
+  }
 
   // Terminate the table with a final blank line to clearly delineate it from
   // later dumps.
@@ -532,7 +611,9 @@
 
 void DWARFDebugLine::LineTable::clear() {
   Prologue.clear();
-  Rows.clear();
+  SLLTRows.clear();
+  ActualRows.clear();
+  LogicalRows.clear();
   Sequences.clear();
 }
 
@@ -548,16 +629,16 @@
   Sequence.reset();
 }
 
-void DWARFDebugLine::ParsingState::appendRowToMatrix() {
-  unsigned RowNumber = LineTable->Rows.size();
-  if (Sequence.Empty) {
+void DWARFDebugLine::ParsingState::appendRowToMatrix(LineTableType RowType) {
+  unsigned RowNumber = LineTable->getRows(RowType).size();
+  if (RowType != LineTableType::LogicalTable && Sequence.Empty) {
     // Record the beginning of instruction sequence.
     Sequence.Empty = false;
     Sequence.LowPC = Row.Address.Address;
     Sequence.FirstRowIndex = RowNumber;
   }
-  LineTable->appendRow(Row);
-  if (Row.EndSequence) {
+  LineTable->appendRow(Row, RowType);
+  if (RowType != LineTableType::LogicalTable && Row.EndSequence) {
     // Record the end of instruction sequence.
     Sequence.HighPC = Row.Address.Address;
     Sequence.LastRowIndex = RowNumber + 1;
@@ -780,34 +861,65 @@
     assert(Prologue.getAddressSize() == 0 ||
            Prologue.getAddressSize() == TableData.getAddressSize());
 
+  int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Prologue.FormParams.Format);
+
   ParsingState State(this, DebugLineOffset, RecoverableErrorHandler);
 
   *OffsetPtr = DebugLineOffset + Prologue.getLength();
+  // TLLT support...
+  uint64_t LogicalEndOffset = *OffsetPtr + Prologue.LogicalTableLength;
+  LineTableType TableType = Prologue.LogicalTableLength > 0
+                                ? LineTableType::LogicalTable
+                                : LineTableType::SingleLevelLineTable;
   if (OS && *OffsetPtr < EndOffset) {
     *OS << '\n';
-    Row::dumpTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0);
+    if (TableType == LineTableType::SingleLevelLineTable)
+      Row::dumpTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0);
+    else
+      Row::dumpLogicalTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0);
   }
   bool TombstonedAddress = false;
   auto EmitRow = [&] {
     if (!TombstonedAddress) {
+      if (TableType == LineTableType::LogicalTable)
+        State.Row.LogicalIndex = State.LineTable->LogicalRows.size() + 1;
       if (Verbose) {
         *OS << "\n";
         OS->indent(12);
       }
-      if (OS)
-        State.Row.dump(*OS);
-      State.appendRowToMatrix();
+      if (OS) {
+        if (TableType == LineTableType::SingleLevelLineTable)
+          State.Row.dump(*OS);
+        else if (TableType == LineTableType::LogicalTable)
+          State.Row.dumpLogical(*OS);
+        else
+          State.Row.dumpActual(*OS);
+      }
+      State.appendRowToMatrix(TableType);
     }
   };
+
   while (*OffsetPtr < EndOffset) {
     DataExtractor::Cursor Cursor(*OffsetPtr);
 
+    // If we're printing the Logical table and have reached the end, switch to
+    // printing the Actual table.
+    if (TableType == LineTableType::LogicalTable &&
+        *OffsetPtr >= LogicalEndOffset) {
+      if (OS) {
+        *OS << '\n';
+        Row::dumpActualTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0);
+      }
+      TableType = LineTableType::ActualTable;
+      State.resetRowAndSequence();
+    }
+
     if (Verbose)
       *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr);
 
     uint64_t OpcodeOffset = *OffsetPtr;
     uint8_t Opcode = TableData.getU8(Cursor);
-    size_t RowCount = Rows.size();
+    size_t RowCount = getRows(TableType).size();
 
     if (Cursor && Verbose)
       *OS << format("%02.02" PRIx8 " ", Opcode);
@@ -1125,6 +1237,30 @@
         }
         break;
 
+      case DW_LNS_inlined_call:
+        if (std::optional<uint32_t> Context =
+                parseULEB128<uint32_t>(TableData, Cursor)) {
+          State.Row.Context = *Context;
+          if (Verbose)
+            *OS << " (" << (uint64_t)State.Row.Context << ")";
+        }
+        if (std::optional<uint64_t> SubprogramOffset =
+                parseULEB128<uint64_t>(TableData, Cursor)) {
+          if (*SubprogramOffset == 0) {
+            State.Row.SubprogramOffset = DWARFFormValue();
+          }
+          else {
+            State.Row.SubprogramOffset =
+              DWARFFormValue::createFromUValue(dwarf::Form::DW_FORM_line_strp,
+                *SubprogramOffset);
+            State.Row.SubprogramOffset.setContextAndUnit(&Ctx, U);
+          }
+          if (Verbose)
+            *OS << format(" (0x%0*" PRIx64 ")", OffsetDumpWidth,
+              (uint64_t)*SubprogramOffset);
+        }
+        break;
+
       default:
         // Handle any unknown standard opcodes here. We know the lengths
         // of such opcodes because they are specified in the prologue
@@ -1172,7 +1308,7 @@
 
     // When a row is added to the matrix, it is also dumped, which includes a
     // new line already, so don't add an extra one.
-    if (Verbose && Rows.size() == RowCount)
+    if (Verbose && getRows(TableType).size() == RowCount)
       *OS << "\n";
 
     // Most parse failures other than when parsing extended opcodes are due to
@@ -1228,6 +1364,7 @@
   // than or equal to Address. This can be computed as upper_bound - 1.
   DWARFDebugLine::Row Row;
   Row.Address = Address;
+  const RowVector &Rows = isTLLT() ? ActualRows : SLLTRows;
   RowIter FirstRow = Rows.begin() + Seq.FirstRowIndex;
   RowIter LastRow = Rows.begin() + Seq.LastRowIndex;
   assert(FirstRow->Address.Address <= Row.Address.Address &&
@@ -1411,6 +1548,7 @@
   if (RowIndex == -1U)
     return false;
   // Take file number and line/column from the row.
+  const RowVector &Rows = isTLLT() ? ActualRows : SLLTRows;
   const auto &Row = Rows[RowIndex];
   if (!getFileNameByIndex(Row.File, CompDir, Kind, Result.FileName))
     return false;
Index: llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
===================================================================
--- llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -1433,7 +1433,11 @@
 
   for (uint32_t RowIndex : RowVector) {
     // Take file number and line/column from the row.
-    const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex];
+    // TLLT: We need to take the line number from the Logicals table.
+    const DWARFDebugLine::Row &Row =
+        LineTable->isTLLT()
+            ? LineTable->LogicalRows[LineTable->ActualRows[RowIndex].Line - 1]
+            : LineTable->SLLTRows[RowIndex];
     DILineInfo Result;
     LineTable->getFileNameByIndex(Row.File, CU->getCompilationDir(),
                                   Spec.FLIKind, Result.FileName);
Index: llvm/lib/CodeGen/CommandFlags.cpp
===================================================================
--- llvm/lib/CodeGen/CommandFlags.cpp
+++ llvm/lib/CodeGen/CommandFlags.cpp
@@ -98,6 +98,7 @@
 CGOPT(bool, EmitCallSiteInfo)
 CGOPT(bool, EnableMachineFunctionSplitter)
 CGOPT(bool, EnableDebugEntryValues)
+CGOPT(bool, EnableTwoLevelLineTables)
 CGOPT(bool, ForceDwarfFrameSection)
 CGOPT(bool, XRayOmitFunctionIndex)
 CGOPT(bool, DebugStrictDwarf)
@@ -449,6 +450,12 @@
       cl::init(false));
   CGBINDOPT(EnableDebugEntryValues);
 
+  static cl::opt<bool> EnableTwoLevelLineTables(
+      "two-level-line-tables",
+      cl::desc("Enable the emission of Two-Level Line Tables for Dwarf5."),
+      cl::init(false));
+  CGBINDOPT(EnableTwoLevelLineTables);
+
   static cl::opt<bool> EnableMachineFunctionSplitter(
       "split-machine-functions",
       cl::desc("Split out cold basic blocks from machine functions based on "
@@ -558,6 +565,7 @@
   Options.EmitAddrsig = getEnableAddrsig();
   Options.EmitCallSiteInfo = getEmitCallSiteInfo();
   Options.EnableDebugEntryValues = getEnableDebugEntryValues();
+  Options.EnableTwoLevelLineTables = getEnableTwoLevelLineTables();
   Options.ForceDwarfFrameSection = getForceDwarfFrameSection();
   Options.XRayOmitFunctionIndex = getXRayOmitFunctionIndex();
   Options.DebugStrictDwarf = getDebugStrictDwarf();
Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
===================================================================
--- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -294,6 +294,39 @@
 
 /// Collects and handles dwarf debug information.
 class DwarfDebug : public DebugHandlerBase {
+  /// TLLT: Current logical location table size.
+  unsigned LogicalsTableSize = 0;
+  /// TLLT: The logical line number associated with the current actual entry.
+  unsigned LastActualEntryLogicalLineRefernce = 0;
+  /// TLLT: Look-up map for existing logical locations.
+  DenseMap<MCDwarfLoc, uint64_t> LogicalsLookup;
+  /// TLLT: Look-up map for inlined call site logical locations.
+  DenseMap<const DILocation *, uint64_t> LogicalsContextLookup;
+
+  /// Reset TLLT state.
+  void TLLTReset();
+  /// Called from TLLTGetOrCreateLogicalsEntryIdx to recursively create logicals
+  /// entries. Does not update the index for the last logical entry; that should
+  /// be set from the caller so that only the outermost line is remembered.
+  uint32_t TLLTGetOrCreateLogicalsEntryIdxImpl(
+      unsigned DwarfVersion, AsmPrinter *Asm, unsigned Line, unsigned Column,
+      unsigned Flags, unsigned Isa, const MDNode *Scope,
+      const DILocation *InlinedAt, DwarfCompileUnit *CU);
+
+public:
+  /// Create a TLLT logical entry if one doesn't already exist for this
+  /// location. Recursively create logical entries for inline callsites if
+  /// needed.
+  uint32_t TLLTGetOrCreateLogicalsEntryIdx(unsigned DwarfVersion,
+                                           AsmPrinter *Asm, unsigned Line,
+                                           unsigned Column, unsigned Flags,
+                                           unsigned Isa, const MDNode *Scope,
+                                           const DILocation *InlinedAt,
+                                           DwarfCompileUnit *CU);
+  /// Return the logical line number associated with the current actual entry.
+  uint32_t TLLTGetCurrentLine() { return LastActualEntryLogicalLineRefernce; }
+
+private:
   /// All DIEValues are allocated through this allocator.
   BumpPtrAllocator DIEValueAllocator;
 
@@ -606,7 +639,7 @@
   /// label that was emitted and which provides correspondence to the
   /// source line list.
   void recordSourceLine(unsigned Line, unsigned Col, const MDNode *Scope,
-                        unsigned Flags);
+                        unsigned Flags, const DILocation *InlinedAt);
 
   /// Populate LexicalScope entries with variables' info.
   void collectEntityInfo(DwarfCompileUnit &TheCU, const DISubprogram *SP,
@@ -757,6 +790,9 @@
   /// Returns the Dwarf Version.
   uint16_t getDwarfVersion() const;
 
+  /// Returns whether we'd like to emit TLLT.
+  bool getEnableTwoLevelLineTables() const;
+
   /// Returns a suitable DWARF form to represent a section offset, i.e.
   /// * DW_FORM_sec_offset for DWARF version >= 4;
   /// * DW_FORM_data8 for 64-bit DWARFv3;
Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
===================================================================
--- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -461,6 +461,8 @@
   Asm->OutStreamer->getContext().setDwarfVersion(DwarfVersion);
   Asm->OutStreamer->getContext().setDwarfFormat(Dwarf64 ? dwarf::DWARF64
                                                         : dwarf::DWARF32);
+  Asm->OutStreamer->getContext().setEnableTwoLevelLineTables(
+      Asm->TM.Options.EnableTwoLevelLineTables);
 }
 
 // Define out of line so we don't have to include DwarfUnit.h in DwarfDebug.h.
@@ -1161,12 +1163,99 @@
   return GVEs;
 }
 
+void DwarfDebug::TLLTReset() {
+  LastActualEntryLogicalLineRefernce = 0;
+  LogicalsTableSize = 0;
+  LogicalsLookup.clear();
+  LogicalsContextLookup.clear();
+}
+
+struct DwarfLocBlockAndFile {
+  StringRef FileName;
+  unsigned FileNo = 1;
+  unsigned Discriminator = 0;
+};
+
+DwarfLocBlockAndFile getDwarfLocBlockAndFile(const MDNode *ScopeMD,
+                                             unsigned Line,
+                                             unsigned DwarfVersion,
+                                             DwarfCompileUnit *CU) {
+  DwarfLocBlockAndFile Data;
+  if (auto *Scope = cast_or_null<DIScope>(ScopeMD)) {
+    Data.FileName = Scope->getFilename();
+    if (Line != 0 && DwarfVersion >= 4)
+      if (auto *LBF = dyn_cast<DILexicalBlockFile>(Scope))
+        Data.Discriminator = LBF->getDiscriminator();
+
+    Data.FileNo = CU->getOrCreateSourceID(Scope->getFile());
+  }
+  return Data;
+}
+
+uint32_t DwarfDebug::TLLTGetOrCreateLogicalsEntryIdx(
+    unsigned DwarfVersion, AsmPrinter *Asm, unsigned Line, unsigned Column,
+    unsigned Flags, unsigned Isa, const MDNode *Scope,
+    const DILocation *InlinedAt, DwarfCompileUnit *CU) {
+  LastActualEntryLogicalLineRefernce = Line;
+  return TLLTGetOrCreateLogicalsEntryIdxImpl(DwarfVersion, Asm, Line, Column,
+                                             Flags, Isa, Scope, InlinedAt, CU);
+}
+
+uint32_t DwarfDebug::TLLTGetOrCreateLogicalsEntryIdxImpl(
+    unsigned DwarfVersion, AsmPrinter *Asm, unsigned Line, unsigned Column,
+    unsigned Flags, unsigned Isa, const MDNode *Scope,
+    const DILocation *InlinedAt, DwarfCompileUnit *CU) {
+  StringRef FunctionName;
+  uint32_t CC = 0;
+  // Check if this is an inlined instruction.
+  if (InlinedAt) {
+    auto R = LogicalsContextLookup.find(InlinedAt);
+    if (R == LogicalsContextLookup.end()) {
+      // TLLT FIXME: Unsure how the CU comes into play if the function has been
+      // inlined from another CU. Leave it unhandled for now.
+      CC = TLLTGetOrCreateLogicalsEntryIdxImpl(
+          DwarfVersion, Asm, InlinedAt->getLine(), InlinedAt->getColumn(),
+          Flags, Isa, Scope, InlinedAt->getInlinedAt(), CU);
+      LogicalsContextLookup.insert(std::make_pair(InlinedAt, CC));
+    } else {
+      CC = R->second;
+    }
+    if (const auto *LocalScope = cast_or_null<DILocalScope>(Scope)) {
+      if (auto *Subprg = LocalScope->getSubprogram())
+        FunctionName = Subprg->getName();
+    } else {
+      static const char *UnknownFunctionName = "???";
+      FunctionName = UnknownFunctionName;
+    }
+  }
+
+  // TLLT FIXME: Unnecessarily constructing and deconstructing this repeatedly.
+  DwarfLocBlockAndFile BlockAndFileData =
+      getDwarfLocBlockAndFile(Scope, Line, DwarfVersion, CU);
+  MCDwarfLoc Loc = MCDwarfLoc(BlockAndFileData.FileNo, Line, Column, Flags, Isa,
+                              BlockAndFileData.Discriminator, CC, FunctionName);
+
+  auto R = LogicalsLookup.find(Loc);
+  if (R == LogicalsLookup.end()) {
+    uint32_t Index = ++LogicalsTableSize;
+    LogicalsLookup.insert(std::make_pair(Loc, Index));
+    Asm->OutStreamer->emitDwarfLogicalLocDirective(
+        Loc.getFileNum(), Loc.getLine(), Loc.getColumn(), Loc.getFlags(),
+        Loc.getIsa(), Loc.getDiscriminator(), BlockAndFileData.FileName,
+        Loc.getContext(), Loc.getFunctionName());
+    return Index;
+  } else {
+    return R->second;
+  }
+}
+
 // Emit all Dwarf sections that should come prior to the content. Create
 // global DIEs and emit initial debug info sections. This is invoked by
 // the target AsmPrinter.
 void DwarfDebug::beginModule(Module *M) {
   DebugHandlerBase::beginModule(M);
-
+  // TLLT FIXME: This may not be the correct/only place to reset?
+  TLLTReset();
   if (!Asm || !MMI->hasDebugInfo())
     return;
 
@@ -2051,7 +2140,9 @@
   // When we emit a line-0 record, we don't update PrevInstLoc; so look at
   // the last line number actually emitted, to see if it was line 0.
   unsigned LastAsmLine =
-      Asm->OutStreamer->getContext().getCurrentDwarfLoc().getLine();
+      getDwarfVersion() >= 5 && getEnableTwoLevelLineTables()
+          ? TLLTGetCurrentLine()
+          : Asm->OutStreamer->getContext().getCurrentDwarfLoc().getLine();
 
   bool PrevInstInSameSection =
       (!PrevInstBB ||
@@ -2065,7 +2156,8 @@
     if ((LastAsmLine == 0 && DL.getLine() != 0) || Flags) {
       // Reinstate the source location but not marked as a statement.
       const MDNode *Scope = DL.getScope();
-      recordSourceLine(DL.getLine(), DL.getCol(), Scope, Flags);
+      recordSourceLine(DL.getLine(), DL.getCol(), Scope, Flags,
+                       DL.getInlinedAt());
     }
     return;
   }
@@ -2092,11 +2184,13 @@
       // Do not update PrevInstLoc, it remembers the last non-0 line.
       const MDNode *Scope = nullptr;
       unsigned Column = 0;
+      const DILocation *InlinedAt = nullptr;
       if (PrevInstLoc) {
         Scope = PrevInstLoc.getScope();
         Column = PrevInstLoc.getCol();
+        InlinedAt = PrevInstLoc.getInlinedAt();
       }
-      recordSourceLine(/*Line=*/0, Column, Scope, /*Flags=*/0);
+      recordSourceLine(/*Line=*/0, Column, Scope, /*Flags=*/0, InlinedAt);
     }
     return;
   }
@@ -2117,7 +2211,7 @@
     Flags |= DWARF2_FLAG_IS_STMT;
 
   const MDNode *Scope = DL.getScope();
-  recordSourceLine(DL.getLine(), DL.getCol(), Scope, Flags);
+  recordSourceLine(DL.getLine(), DL.getCol(), Scope, Flags, DL.getInlinedAt());
 
   // If we're not at line 0, remember this location.
   if (DL.getLine())
@@ -2157,24 +2251,24 @@
 
 /// Register a source line with debug info. Returns the  unique label that was
 /// emitted and which provides correspondence to the source line list.
-static void recordSourceLine(AsmPrinter &Asm, unsigned Line, unsigned Col,
-                             const MDNode *S, unsigned Flags, unsigned CUID,
-                             uint16_t DwarfVersion,
-                             ArrayRef<std::unique_ptr<DwarfCompileUnit>> DCUs) {
-  StringRef Fn;
-  unsigned FileNo = 1;
-  unsigned Discriminator = 0;
-  if (auto *Scope = cast_or_null<DIScope>(S)) {
-    Fn = Scope->getFilename();
-    if (Line != 0 && DwarfVersion >= 4)
-      if (auto *LBF = dyn_cast<DILexicalBlockFile>(Scope))
-        Discriminator = LBF->getDiscriminator();
-
-    FileNo = static_cast<DwarfCompileUnit &>(*DCUs[CUID])
-                 .getOrCreateSourceID(Scope->getFile());
+static void recordSourceLine(DwarfDebug *DD, AsmPrinter &Asm, unsigned Line,
+                             unsigned Col, const MDNode *S, unsigned Flags,
+                             unsigned CUID, uint16_t DwarfVersion,
+                             ArrayRef<std::unique_ptr<DwarfCompileUnit>> DCUs,
+                             const DILocation *InlinedAt) {
+  if (DwarfVersion < 5 || !DD->getEnableTwoLevelLineTables()) {
+    auto FileBlockData =
+        getDwarfLocBlockAndFile(S, Line, DwarfVersion, DCUs[CUID].get());
+    Asm.OutStreamer->emitDwarfLocDirective(
+        FileBlockData.FileNo, Line, Col, Flags, 0, FileBlockData.Discriminator,
+        FileBlockData.FileName);
+  } else {
+    const MDNode *Scope = cast_or_null<DIScope>(S);
+    auto LogicalsIndex = DD->TLLTGetOrCreateLogicalsEntryIdx(
+        DwarfVersion, &Asm, Line, Col, Flags, /*Isa=*/0, Scope, InlinedAt,
+        DCUs[CUID].get());
+    Asm.OutStreamer->emitDwarfActualLocDirective(LogicalsIndex, Flags);
   }
-  Asm.OutStreamer->emitDwarfLocDirective(FileNo, Line, Col, Flags, 0,
-                                         Discriminator, Fn);
 }
 
 DebugLoc DwarfDebug::emitInitialLocDirective(const MachineFunction &MF,
@@ -2196,8 +2290,9 @@
     // We'd like to list the prologue as "not statements" but GDB behaves
     // poorly if we do that. Revisit this with caution/GDB (7.5+) testing.
     const DISubprogram *SP = PrologEndLoc->getInlinedAtScope()->getSubprogram();
-    ::recordSourceLine(*Asm, SP->getScopeLine(), 0, SP, DWARF2_FLAG_IS_STMT,
-                       CUID, getDwarfVersion(), getUnits());
+    ::recordSourceLine(this, *Asm, SP->getScopeLine(), 0, SP,
+                       DWARF2_FLAG_IS_STMT, CUID, getDwarfVersion(), getUnits(),
+                       PrologEndLoc->getInlinedAt());
     return PrologEndLoc;
   }
   return DebugLoc();
@@ -2346,10 +2441,10 @@
 // Register a source line with debug info. Returns the  unique label that was
 // emitted and which provides correspondence to the source line list.
 void DwarfDebug::recordSourceLine(unsigned Line, unsigned Col, const MDNode *S,
-                                  unsigned Flags) {
-  ::recordSourceLine(*Asm, Line, Col, S, Flags,
+                                  unsigned Flags, const DILocation *InlinedAt) {
+  ::recordSourceLine(this, *Asm, Line, Col, S, Flags,
                      Asm->OutStreamer->getContext().getDwarfCompileUnitID(),
-                     getDwarfVersion(), getUnits());
+                     getDwarfVersion(), getUnits(), InlinedAt);
 }
 
 //===----------------------------------------------------------------------===//
@@ -3575,6 +3670,9 @@
 uint16_t DwarfDebug::getDwarfVersion() const {
   return Asm->OutStreamer->getContext().getDwarfVersion();
 }
+bool DwarfDebug::getEnableTwoLevelLineTables() const {
+  return Asm->OutStreamer->getContext().getEnableTwoLevelLineTables();
+}
 
 dwarf::Form DwarfDebug::getDwarfSectionOffsetForm() const {
   if (Asm->getDwarfVersion() >= 4)
Index: llvm/include/llvm/Target/TargetOptions.h
===================================================================
--- llvm/include/llvm/Target/TargetOptions.h
+++ llvm/include/llvm/Target/TargetOptions.h
@@ -139,7 +139,8 @@
           EnableMachineOutliner(false), EnableMachineFunctionSplitter(false),
           SupportsDefaultOutlining(false), EmitAddrsig(false),
           EmitCallSiteInfo(false), SupportsDebugEntryValues(false),
-          EnableDebugEntryValues(false), ValueTrackingVariableLocations(false),
+          EnableDebugEntryValues(false), EnableTwoLevelLineTables(false),
+          ValueTrackingVariableLocations(false),
           ForceDwarfFrameSection(false), XRayOmitFunctionIndex(false),
           DebugStrictDwarf(false), Hotpatch(false),
           PPCGenScalarMASSEntries(false), JMCInstrument(false),
@@ -320,11 +321,13 @@
     /// of debug entry values even if the target does not officially support
     /// it. Useful for testing purposes only. This flag should never be checked
     /// directly, always use \ref ShouldEmitDebugEntryValues instead.
-     unsigned EnableDebugEntryValues : 1;
+    unsigned EnableDebugEntryValues : 1;
     /// NOTE: There are targets that still do not support the debug entry values
     /// production.
     bool ShouldEmitDebugEntryValues() const;
 
+    unsigned EnableTwoLevelLineTables : 1;
+
     // When set to true, use experimental new debug variable location tracking,
     // which seeks to follow the values of variables rather than their location,
     // post isel.
Index: llvm/include/llvm/MC/MCStreamer.h
===================================================================
--- llvm/include/llvm/MC/MCStreamer.h
+++ llvm/include/llvm/MC/MCStreamer.h
@@ -941,6 +941,13 @@
                                      unsigned Isa, unsigned Discriminator,
                                      StringRef FileName);
 
+  virtual void
+  emitDwarfLogicalLocDirective(unsigned FileNo, unsigned Line, unsigned Column,
+                               unsigned Flags, unsigned Isa,
+                               unsigned Discriminator, StringRef FileName,
+                               unsigned Context, StringRef FunctionName);
+  virtual void emitDwarfActualLocDirective(unsigned Line, unsigned Flags);
+
   /// Associate a filename with a specified logical file number, and also
   /// specify that file's checksum information.  This implements the '.cv_file 4
   /// "foo.c"' assembler directive. Returns true on success.
Index: llvm/include/llvm/MC/MCDwarf.h
===================================================================
--- llvm/include/llvm/MC/MCDwarf.h
+++ llvm/include/llvm/MC/MCDwarf.h
@@ -14,10 +14,13 @@
 #ifndef LLVM_MC_MCDWARF_H
 #define LLVM_MC_MCDWARF_H
 
+#include "llvm/ADT/DenseMapInfo.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Metadata.h"
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MD5.h"
@@ -32,6 +35,7 @@
 namespace llvm {
 
 template <typename T> class ArrayRef;
+class AsmPrinter;
 class MCAsmBackend;
 class MCContext;
 class MCObjectStreamer;
@@ -64,6 +68,8 @@
 
   /// Emit a reference to the string.
   void emitRef(MCStreamer *MCOS, StringRef Path);
+  /// Add the string to .debug_line_str and return a reference to it.
+  size_t getRef(MCStreamer *MCOS, StringRef Path);
 
   /// Emit the .debug_line_str section if appropriate.
   void emitSection(MCStreamer *MCOS);
@@ -106,6 +112,8 @@
   uint8_t Flags;
   uint8_t Isa;
   uint32_t Discriminator;
+  uint32_t Context;
+  StringRef FunctionName;
 
 // Flag that indicates the initial value of the is_stmt_start flag.
 #define DWARF2_LINE_DEFAULT_IS_STMT 1
@@ -118,16 +126,27 @@
 private: // MCContext manages these
   friend class MCContext;
   friend class MCDwarfLineEntry;
+  // Allow the default copy constructor and assignment operator to be used
+  // for an MCDwarfLoc object.
 
+public:
+  // TLLT FIXME: This was private - and should probably be returned to private.
   MCDwarfLoc(unsigned fileNum, unsigned line, unsigned column, unsigned flags,
-             unsigned isa, unsigned discriminator)
+             unsigned isa, unsigned discriminator, unsigned Context = 0,
+             StringRef FunctionName = StringRef())
       : FileNum(fileNum), Line(line), Column(column), Flags(flags), Isa(isa),
-        Discriminator(discriminator) {}
+        Discriminator(discriminator), Context(Context),
+        FunctionName(FunctionName) {}
 
-  // Allow the default copy constructor and assignment operator to be used
-  // for an MCDwarfLoc object.
+  MCDwarfLoc() = default; ///< TLLT: For usage in containers.
+  static MCDwarfLoc getTombstoneKey() {
+    return MCDwarfLoc(-1, -1, -1, -1, -1, -1, -1);
+  }
+  static MCDwarfLoc getEmptyKey() {
+    return MCDwarfLoc(-2, -2, -2, -2, -2, -2, -2);
+  }
+  friend DenseMapInfo<MCDwarfLoc>;
 
-public:
   /// Get the FileNum of this MCDwarfLoc.
   unsigned getFileNum() const { return FileNum; }
 
@@ -146,6 +165,12 @@
   /// Get the Discriminator of this MCDwarfLoc.
   unsigned getDiscriminator() const { return Discriminator; }
 
+  /// Get the Context of this MCDwarfLoc.
+  unsigned getContext() const { return Context; }
+
+  /// Get the FunctionName of this MCDwarfLoc.
+  StringRef getFunctionName() const { return FunctionName; }
+
   /// Set the FileNum of this MCDwarfLoc.
   void setFileNum(unsigned fileNum) { FileNum = fileNum; }
 
@@ -174,6 +199,33 @@
   void setDiscriminator(unsigned discriminator) {
     Discriminator = discriminator;
   }
+
+  /// Set the Context of this MCDwarfLoc.
+  void setContext(unsigned context) {
+    Context = context;
+  }
+
+  /// Set the FunctionName of this MCDwarfLoc.
+  void setFunctionName(StringRef functionName) { FunctionName = functionName; }
+};
+
+template <> struct DenseMapInfo<MCDwarfLoc> {
+  static inline MCDwarfLoc getEmptyKey() { return MCDwarfLoc::getEmptyKey(); }
+  static inline MCDwarfLoc getTombstoneKey() {
+    return MCDwarfLoc::getTombstoneKey();
+  }
+  static unsigned getHashValue(const MCDwarfLoc &Val) {
+    return hash_combine(Val.getFileNum(), Val.Line, Val.Column, Val.Flags,
+                        Val.Isa, Val.Discriminator, Val.Context);
+  }
+  static bool isEqual(const MCDwarfLoc &LHS, const MCDwarfLoc &RHS) {
+    return LHS.FileNum == RHS.FileNum && LHS.Line == RHS.Line &&
+           LHS.Column == RHS.Column && LHS.Flags == RHS.Flags &&
+           LHS.Isa == RHS.Isa && LHS.Discriminator == RHS.Discriminator &&
+           LHS.Context == RHS.Context;
+    // TLLT Note: FunctionName should be equal if Context is equal,
+    // so we shouldn't need to compare it.
+  }
 };
 
 /// Instances of this class represent the line information for
@@ -220,6 +272,12 @@
   void addLineEntry(const MCDwarfLineEntry &LineEntry, MCSection *Sec) {
     MCLineDivisions[Sec].push_back(LineEntry);
   }
+  void addActualLineEntry(const MCDwarfLineEntry &LineEntry, MCSection *Sec) {
+    MCActualLineDivisions[Sec].push_back(LineEntry);
+  }
+  void addLogicalLineEntry(const MCDwarfLineEntry &LineEntry, MCSection *Sec) {
+    MCLogicalLineDivisions[Sec].push_back(LineEntry);
+  }
 
   // Add an end entry by cloning the last entry, if exists, for the section
   // the given EndLabel belongs to. The label is replaced by the given EndLabel.
@@ -233,12 +291,20 @@
 private:
   // A collection of MCDwarfLineEntry for each section.
   MCLineDivisionMap MCLineDivisions;
+  MCLineDivisionMap MCLogicalLineDivisions;
+  MCLineDivisionMap MCActualLineDivisions;
 
 public:
   // Returns the collection of MCDwarfLineEntry for a given Compile Unit ID.
   const MCLineDivisionMap &getMCLineEntries() const {
     return MCLineDivisions;
   }
+  const MCLineDivisionMap &getMCLogicalLineEntries() const {
+    return MCLogicalLineDivisions;
+  }
+  const MCLineDivisionMap &getMCActualLineEntries() const {
+    return MCActualLineDivisions;
+  }
 };
 
 struct MCDwarfLineTableParams {
@@ -246,7 +312,7 @@
   /// Note: If you want to change this, you'll have to update the
   /// "StandardOpcodeLengths" table that is emitted in
   /// \c Emit().
-  uint8_t DWARF2LineOpcodeBase = 13;
+  uint8_t DWARF2LineOpcodeBase = 14;
   /// Minimum line offset in a special line info. opcode.  The value
   /// -5 was chosen to give a reasonable range of values.
   int8_t DWARF2LineBase = -5;
@@ -273,10 +339,10 @@
                                 std::optional<MD5::MD5Result> Checksum,
                                 std::optional<StringRef> Source,
                                 uint16_t DwarfVersion, unsigned FileNumber = 0);
-  std::pair<MCSymbol *, MCSymbol *>
+  std::tuple<MCSymbol *, MCSymbol *, MCSymbol *>
   Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
        std::optional<MCDwarfLineStr> &LineStr) const;
-  std::pair<MCSymbol *, MCSymbol *>
+  std::tuple<MCSymbol *, MCSymbol *, MCSymbol *>
   Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params,
        ArrayRef<char> SpecialOpcodeLengths,
        std::optional<MCDwarfLineStr> &LineStr) const;
@@ -358,7 +424,8 @@
   // This emits a single line table associated with a given Section.
   static void
   emitOne(MCStreamer *MCOS, MCSection *Section,
-          const MCLineSection::MCDwarfLineEntryCollection &LineEntries);
+          const MCLineSection::MCDwarfLineEntryCollection &LineEntries,
+          std::optional<MCDwarfLineStr> &LineStr);
 
   Expected<unsigned> tryGetFile(StringRef &Directory, StringRef &FileName,
                                 std::optional<MD5::MD5Result> Checksum,
Index: llvm/include/llvm/MC/MCContext.h
===================================================================
--- llvm/include/llvm/MC/MCContext.h
+++ llvm/include/llvm/MC/MCContext.h
@@ -205,6 +205,11 @@
   MCDwarfLoc CurrentDwarfLoc;
   bool DwarfLocSeen = false;
 
+  SmallVector<MCDwarfLoc> CurrentDwarfLogicalLocs;
+
+  MCDwarfLoc CurrentDwarfActualLoc;
+  bool DwarfActualLocSeen = false;
+
   /// Generate dwarf debugging info for assembly source files.
   bool GenDwarfForAssembly = false;
 
@@ -230,6 +235,8 @@
   /// The maximum version of dwarf that we should emit.
   uint16_t DwarfVersion = 4;
 
+  bool EnableTwoLevelLineTables = false;
+
   /// The format of dwarf that we emit.
   dwarf::DwarfFormat DwarfFormat = dwarf::DWARF32;
 
@@ -776,14 +783,58 @@
     CurrentDwarfLoc.setFlags(Flags);
     CurrentDwarfLoc.setIsa(Isa);
     CurrentDwarfLoc.setDiscriminator(Discriminator);
+    CurrentDwarfLoc.setContext(0);
+    CurrentDwarfLoc.setFunctionName("");
     DwarfLocSeen = true;
   }
 
+  /// TLLT - Set info for logicals table entry.
+  /// FIXME: Merge this with setCurrentDwarfLoc?
+  void addCurrentDwarfLogicalLoc(unsigned FileNum, unsigned Line,
+                                 unsigned Column, unsigned Flags, unsigned Isa,
+                                 unsigned Discriminator, unsigned Context,
+                                 StringRef FunctionName) {
+    MCDwarfLoc Loc;
+    Loc.setFileNum(FileNum);
+    Loc.setLine(Line);
+    Loc.setColumn(Column);
+    Loc.setFlags(Flags);
+    Loc.setIsa(Isa);
+    Loc.setDiscriminator(Discriminator);
+    Loc.setContext(Context);
+    Loc.setFunctionName(FunctionName);
+    CurrentDwarfLogicalLocs.push_back(Loc);
+  }
+
+  /// TLLT - Set info for actuals table entry.
+  /// FIXME: Do we need FileNum?
+  void setCurrentDwarfActualLoc(unsigned Line, unsigned Flags) {
+    CurrentDwarfActualLoc.setFileNum(0);
+    CurrentDwarfActualLoc.setLine(Line);
+    CurrentDwarfActualLoc.setColumn(0);
+    CurrentDwarfActualLoc.setFlags(Flags);
+    CurrentDwarfActualLoc.setIsa(0);
+    CurrentDwarfActualLoc.setDiscriminator(0);
+    CurrentDwarfActualLoc.setContext(0);
+    CurrentDwarfActualLoc.setFunctionName("");
+    DwarfActualLocSeen = true;
+  }
+
   void clearDwarfLocSeen() { DwarfLocSeen = false; }
 
   bool getDwarfLocSeen() { return DwarfLocSeen; }
   const MCDwarfLoc &getCurrentDwarfLoc() { return CurrentDwarfLoc; }
 
+  void clearDwarfLogicalLocsSeen() { CurrentDwarfLogicalLocs.clear(); }
+  bool getDwarfLogicalLocsSeen() { return !CurrentDwarfLogicalLocs.empty(); }
+  const SmallVectorImpl<MCDwarfLoc> &getCurrentDwarfLogicalsLoc() {
+    return CurrentDwarfLogicalLocs;
+  }
+
+  void clearDwarfActualLocSeen() { DwarfActualLocSeen = false; }
+  bool getDwarfActualLocSeen() { return DwarfActualLocSeen; }
+  const MCDwarfLoc &getCurrentDwarfActualLoc() { return CurrentDwarfActualLoc; }
+
   bool getGenDwarfForAssembly() { return GenDwarfForAssembly; }
   void setGenDwarfForAssembly(bool Value) { GenDwarfForAssembly = Value; }
   unsigned getGenDwarfFileNumber() { return GenDwarfFileNumber; }
@@ -827,6 +878,11 @@
   void setDwarfVersion(uint16_t v) { DwarfVersion = v; }
   uint16_t getDwarfVersion() const { return DwarfVersion; }
 
+  void setEnableTwoLevelLineTables(bool tllt) {
+    EnableTwoLevelLineTables = tllt;
+  }
+  bool getEnableTwoLevelLineTables() const { return EnableTwoLevelLineTables; }
+
   /// @}
 
   StringRef getSecureLogFile() { return SecureLogFile; }
Index: llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
===================================================================
--- llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
+++ llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
@@ -77,6 +77,8 @@
   dwarf::Form getForm() const { return Form; }
   uint64_t getRawUValue() const { return Value.uval; }
 
+  void setContextAndUnit(const DWARFContext *c, const DWARFUnit *u) { U = u; C = c; }
+
   bool isFormClass(FormClass FC) const;
   const DWARFUnit *getUnit() const { return U; }
   void dump(raw_ostream &OS, DIDumpOptions DumpOpts = DIDumpOptions()) const;
Index: llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
===================================================================
--- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
+++ llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
@@ -27,6 +27,12 @@
 
 class DWARFDebugLine {
 public:
+  enum class LineTableType {
+    SingleLevelLineTable,
+    LogicalTable,
+    ActualTable,
+  };
+
   struct FileNameEntry {
     FileNameEntry() = default;
 
@@ -69,6 +75,13 @@
     /// The number of bytes following the prologue_length field to the beginning
     /// of the first byte of the statement program itself.
     uint64_t PrologueLength;
+    /// In v6, the number of bytes following the first byte of the Logical
+    /// statement program to the first byte of the Actuals statement program if
+    /// this is a TLLT, or 0 otherwise.
+    uint64_t LogicalTableLength;
+    /// In v6, the format of the function name register in the Logical statement
+    /// program.
+    dwarf::Form FunctionNameForm;
     /// In v5, size in bytes of a segment selector.
     uint8_t SegSelectorSize;
     /// The size in bytes of the smallest target machine instruction. Statement
@@ -136,14 +149,26 @@
     void postAppend();
     void reset(bool DefaultIsStmt);
     void dump(raw_ostream &OS) const;
+    void dumpLogical(raw_ostream &OS) const;
+    void dumpActual(raw_ostream &OS) const;
 
     static void dumpTableHeader(raw_ostream &OS, unsigned Indent);
+    static void dumpLogicalTableHeader(raw_ostream &OS, unsigned Indent);
+    static void dumpActualTableHeader(raw_ostream &OS, unsigned Indent);
 
     static bool orderByAddress(const Row &LHS, const Row &RHS) {
       return std::tie(LHS.Address.SectionIndex, LHS.Address.Address) <
              std::tie(RHS.Address.SectionIndex, RHS.Address.Address);
     }
 
+    /// The row index that this occupies in the Logical table if it is a Logical
+    /// row, or zero otherwise.
+    uint32_t LogicalIndex;
+    /// The logical index that this is inlined at.
+    uint32_t Context;
+    /// The offset into .debug_line_str of this inlined instruction's function
+    /// name, or 0 otherwise.
+    DWARFFormValue SubprogramOffset;
     /// The program-counter value corresponding to a machine instruction
     /// generated by the compiler and section index pointing to the section
     /// containg this PC. If relocation information is present then section
@@ -190,6 +215,7 @@
   /// Represents a series of contiguous machine instructions. Line table for
   /// each compilation unit may consist of multiple sequences, which are not
   /// guaranteed to be in the order of ascending instruction address.
+  /// Applies to either the Single-Level Line Table or the Actuals table.
   struct Sequence {
     Sequence();
 
@@ -228,7 +254,9 @@
     /// Represents an invalid row
     const uint32_t UnknownRowIndex = UINT32_MAX;
 
-    void appendRow(const DWARFDebugLine::Row &R) { Rows.push_back(R); }
+    void appendRow(const DWARFDebugLine::Row &R, LineTableType Type) {
+      getRows(Type).push_back(R);
+    }
 
     void appendSequence(const DWARFDebugLine::Sequence &S) {
       Sequences.push_back(S);
@@ -272,6 +300,8 @@
     bool getDirectoryForEntry(const FileNameEntry &Entry,
                               std::string &Directory) const;
 
+    bool isTLLT() const { return Prologue.LogicalTableLength != 0; }
+
     void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const;
     void clear();
 
@@ -286,8 +316,29 @@
     using SequenceVector = std::vector<Sequence>;
     using SequenceIter = SequenceVector::const_iterator;
 
+    const RowVector &getRows(LineTableType RowType) const {
+      if (RowType == LineTableType::SingleLevelLineTable)
+        return SLLTRows;
+      if (RowType == LineTableType::LogicalTable)
+        return LogicalRows;
+      if (RowType == LineTableType::ActualTable)
+        return ActualRows;
+      llvm_unreachable("Unsupported line table type");
+    }
+    RowVector &getRows(LineTableType RowType) {
+      if (RowType == LineTableType::SingleLevelLineTable)
+        return SLLTRows;
+      if (RowType == LineTableType::LogicalTable)
+        return LogicalRows;
+      if (RowType == LineTableType::ActualTable)
+        return ActualRows;
+      llvm_unreachable("Unsupported line table type");
+    }
+
     struct Prologue Prologue;
-    RowVector Rows;
+    RowVector SLLTRows;
+    RowVector LogicalRows;
+    RowVector ActualRows;
     SequenceVector Sequences;
 
   private:
@@ -371,7 +422,7 @@
                  function_ref<void(Error)> ErrorHandler);
 
     void resetRowAndSequence();
-    void appendRowToMatrix();
+    void appendRowToMatrix(LineTableType RowType);
 
     /// Advance the address by the \p OperationAdvance value. \returns the
     /// amount advanced by.
Index: llvm/include/llvm/CodeGen/CommandFlags.h
===================================================================
--- llvm/include/llvm/CodeGen/CommandFlags.h
+++ llvm/include/llvm/CodeGen/CommandFlags.h
@@ -131,6 +131,8 @@
 
 bool getEnableDebugEntryValues();
 
+bool getEnableTwoLevelLineTables();
+
 bool getValueTrackingVariableLocations();
 std::optional<bool> getExplicitValueTrackingVariableLocations();
 
Index: llvm/include/llvm/BinaryFormat/Dwarf.def
===================================================================
--- llvm/include/llvm/BinaryFormat/Dwarf.def
+++ llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -1024,6 +1024,8 @@
 HANDLE_DW_LNE(0x03, define_file)
 // New in DWARF v4:
 HANDLE_DW_LNE(0x04, set_discriminator)
+// Experimental, proposed for DWARF v6:
+HANDLE_DW_LNE(0x05, set_function_name)
 
 // Line Number Standard Opcode Encodings.
 HANDLE_DW_LNS(0x00, extended_op)
@@ -1040,6 +1042,8 @@
 HANDLE_DW_LNS(0x0a, set_prologue_end)
 HANDLE_DW_LNS(0x0b, set_epilogue_begin)
 HANDLE_DW_LNS(0x0c, set_isa)
+// Experimental, proposed for DWARF v6:
+HANDLE_DW_LNS(0x0d, inlined_call)
 
 // DWARF v5 Line number header entry format.
 HANDLE_DW_LNCT(0x01, path)
Index: lldb/source/Symbol/LineTable.cpp
===================================================================
--- lldb/source/Symbol/LineTable.cpp
+++ lldb/source/Symbol/LineTable.cpp
@@ -28,8 +28,37 @@
   llvm::stable_sort(sequences, less_than_bp);
   for (const auto &sequence : sequences) {
     LineSequenceImpl *seq = static_cast<LineSequenceImpl *>(sequence.get());
-    m_entries.insert(m_entries.end(), seq->m_entries.begin(),
-                     seq->m_entries.end());
+    get_default_table().insert(get_default_table().end(),
+                               seq->m_entries.begin(), seq->m_entries.end());
+  }
+}
+
+LineTable::LineTable(CompileUnit *comp_unit,
+                     std::vector<std::unique_ptr<LineSequence>> &&logicals,
+                     std::vector<std::unique_ptr<LineSequence>> &&actuals)
+    : m_comp_unit(comp_unit), m_TLLTLogicals(), m_TLLTActuals(),
+      m_isTLLT(true) {
+  LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);
+
+  // TLLT: Do not re-order the logicals table or everything will be broken
+  // since actuals use row-based index into it. Furthermore, the address-order
+  // of these entries shouldn't matter since stepping will use the actuals
+  // table rather than logical.
+  // NOTE: This probably causes loads of assumption violations in LLDB so we'll
+  // have to be really careful here.
+  // llvm::stable_sort(logicals, less_than_bp);
+
+  for (const auto &sequence : logicals) {
+    LineSequenceImpl *seq = static_cast<LineSequenceImpl *>(sequence.get());
+    m_TLLTLogicals.insert(m_TLLTLogicals.end(), seq->m_entries.begin(),
+                          seq->m_entries.end());
+  }
+  // It's okay to sort the actuals table.
+  llvm::stable_sort(actuals, less_than_bp);
+  for (const auto &sequence : actuals) {
+    LineSequenceImpl *seq = static_cast<LineSequenceImpl *>(sequence.get());
+    m_TLLTActuals.insert(m_TLLTActuals.end(), seq->m_entries.begin(),
+                         seq->m_entries.end());
   }
 }
 
@@ -48,12 +77,12 @@
 
   LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);
   entry_collection::iterator pos =
-      llvm::upper_bound(m_entries, entry, less_than_bp);
+      llvm::upper_bound(get_default_table(), entry, less_than_bp);
 
   //  Stream s(stdout);
   //  s << "\n\nBefore:\n";
   //  Dump (&s, Address::DumpStyleFileAddress);
-  m_entries.insert(pos, entry);
+  get_default_table().insert(pos, entry);
   //  s << "After:\n";
   //  Dump (&s, Address::DumpStyleFileAddress);
 }
@@ -70,7 +99,7 @@
     LineSequence *sequence, lldb::addr_t file_addr, uint32_t line,
     uint16_t column, uint16_t file_idx, bool is_start_of_statement,
     bool is_start_of_basic_block, bool is_prologue_end, bool is_epilogue_begin,
-    bool is_terminal_entry) {
+    bool is_terminal_entry, bool merge_adjacent_equ_file_addr) {
   assert(sequence != nullptr);
   LineSequenceImpl *seq = reinterpret_cast<LineSequenceImpl *>(sequence);
   Entry entry(file_addr, line, column, file_idx, is_start_of_statement,
@@ -86,7 +115,12 @@
   // here to avoid these kinds of inconsistencies. We will need tor revisit
   // this if the DWARF line tables are updated to allow multiple entries at the
   // same address legally.
-  if (!entries.empty() && entries.back().file_addr == file_addr) {
+
+  // TLLT: merge_adjacent_equ_file_addr is false for the logicals table, in
+  // which we expect to see entries with the same file_addr (e.g. both for an
+  // inlined call site and the inlined source line).
+  if (merge_adjacent_equ_file_addr && !entries.empty() &&
+      entries.back().file_addr == file_addr) {
     // GCC don't use the is_prologue_end flag to mark the first instruction
     // after the prologue.
     // Instead of it it is issuing a line table entry for the first instruction
@@ -111,16 +145,16 @@
 
   // If the first entry address in this sequence is greater than or equal to
   // the address of the last item in our entry collection, just append.
-  if (m_entries.empty() ||
-      !Entry::EntryAddressLessThan(entry, m_entries.back())) {
-    m_entries.insert(m_entries.end(), seq->m_entries.begin(),
-                     seq->m_entries.end());
+  if (get_default_table().empty() ||
+      !Entry::EntryAddressLessThan(entry, get_default_table().back())) {
+    get_default_table().insert(get_default_table().end(),
+                               seq->m_entries.begin(), seq->m_entries.end());
     return;
   }
 
   // Otherwise, find where this belongs in the collection
-  entry_collection::iterator begin_pos = m_entries.begin();
-  entry_collection::iterator end_pos = m_entries.end();
+  entry_collection::iterator begin_pos = get_default_table().begin();
+  entry_collection::iterator end_pos = get_default_table().end();
   LineTable::Entry::LessThanBinaryPredicate less_than_bp(this);
   entry_collection::iterator pos =
       upper_bound(begin_pos, end_pos, entry, less_than_bp);
@@ -139,7 +173,7 @@
     assert(prev_pos->is_terminal_entry);
   }
 #endif
-  m_entries.insert(pos, seq->m_entries.begin(), seq->m_entries.end());
+  get_default_table().insert(pos, seq->m_entries.begin(), seq->m_entries.end());
 }
 
 LineTable::Entry::LessThanBinaryPredicate::LessThanBinaryPredicate(
@@ -177,7 +211,7 @@
 uint32_t LineTable::GetSize() const { return m_entries.size(); }
 
 bool LineTable::GetLineEntryAtIndex(uint32_t idx, LineEntry &line_entry) {
-  if (idx < m_entries.size()) {
+  if (idx < get_default_table().size()) {
     ConvertEntryAtIndexToLineEntry(idx, line_entry);
     return true;
   }
@@ -197,8 +231,8 @@
     Entry search_entry;
     search_entry.file_addr = so_addr.GetFileAddress();
     if (search_entry.file_addr != LLDB_INVALID_ADDRESS) {
-      entry_collection::const_iterator begin_pos = m_entries.begin();
-      entry_collection::const_iterator end_pos = m_entries.end();
+      entry_collection::const_iterator begin_pos = get_default_table().begin();
+      entry_collection::const_iterator end_pos = get_default_table().end();
       entry_collection::const_iterator pos = lower_bound(
           begin_pos, end_pos, search_entry, Entry::EntryAddressLessThan);
       if (pos != end_pos) {
@@ -259,10 +293,10 @@
 
 bool LineTable::ConvertEntryAtIndexToLineEntry(uint32_t idx,
                                                LineEntry &line_entry) {
-  if (idx >= m_entries.size())
+  if (idx >= get_default_table().size())
     return false;
 
-  const Entry &entry = m_entries[idx];
+  const Entry &entry = get_default_table()[idx];
   ModuleSP module_sp(m_comp_unit->GetModule());
   if (!module_sp)
     return false;
@@ -282,8 +316,8 @@
   if (entry.is_terminal_entry)
     line_entry.range.GetBaseAddress().Slide(1);
 
-  if (!entry.is_terminal_entry && idx + 1 < m_entries.size())
-    line_entry.range.SetByteSize(m_entries[idx + 1].file_addr -
+  if (!entry.is_terminal_entry && idx + 1 < get_default_table().size())
+    line_entry.range.SetByteSize(get_default_table()[idx + 1].file_addr -
                                  entry.file_addr);
   else
     line_entry.range.SetByteSize(0);
@@ -332,17 +366,17 @@
     sc_list.Clear();
 
   size_t num_added = 0;
-  const size_t count = m_entries.size();
+  const size_t count = get_default_table().size();
   if (count > 0) {
     SymbolContext sc(m_comp_unit);
 
     for (size_t idx = 0; idx < count; ++idx) {
       // Skip line table rows that terminate the previous row
       // (is_terminal_entry is non-zero)
-      if (m_entries[idx].is_terminal_entry)
+      if (get_default_table()[idx].is_terminal_entry)
         continue;
 
-      if (m_entries[idx].file_idx == file_idx) {
+      if (get_default_table()[idx].file_idx == file_idx) {
         if (ConvertEntryAtIndexToLineEntry(idx, sc.line_entry)) {
           ++num_added;
           sc_list.Append(sc);
@@ -355,7 +389,7 @@
 
 void LineTable::Dump(Stream *s, Target *target, Address::DumpStyle style,
                      Address::DumpStyle fallback_style, bool show_line_ranges) {
-  const size_t count = m_entries.size();
+  const size_t count = get_default_table().size();
   LineEntry line_entry;
   FileSpec prev_file;
   for (size_t idx = 0; idx < count; ++idx) {
@@ -369,7 +403,7 @@
 
 void LineTable::GetDescription(Stream *s, Target *target,
                                DescriptionLevel level) {
-  const size_t count = m_entries.size();
+  const size_t count = get_default_table().size();
   LineEntry line_entry;
   for (size_t idx = 0; idx < count; ++idx) {
     ConvertEntryAtIndexToLineEntry(idx, line_entry);
@@ -384,11 +418,11 @@
     file_ranges.Clear();
   const size_t initial_count = file_ranges.GetSize();
 
-  const size_t count = m_entries.size();
+  const size_t count = get_default_table().size();
   LineEntry line_entry;
   FileAddressRanges::Entry range(LLDB_INVALID_ADDRESS, 0);
   for (size_t idx = 0; idx < count; ++idx) {
-    const Entry &entry = m_entries[idx];
+    const Entry &entry = get_default_table()[idx];
 
     if (entry.is_terminal_entry) {
       if (range.GetRangeBase() != LLDB_INVALID_ADDRESS) {
@@ -406,7 +440,7 @@
 LineTable *LineTable::LinkLineTable(const FileRangeMap &file_range_map) {
   std::unique_ptr<LineTable> line_table_up(new LineTable(m_comp_unit));
   LineSequenceImpl sequence;
-  const size_t count = m_entries.size();
+  const size_t count = get_default_table().size();
   LineEntry line_entry;
   const FileRangeMap::Entry *file_range_entry = nullptr;
   const FileRangeMap::Entry *prev_file_range_entry = nullptr;
@@ -414,7 +448,7 @@
   bool prev_entry_was_linked = false;
   bool range_changed = false;
   for (size_t idx = 0; idx < count; ++idx) {
-    const Entry &entry = m_entries[idx];
+    const Entry &entry = get_default_table()[idx];
 
     const bool end_sequence = entry.is_terminal_entry;
     const lldb::addr_t lookup_file_addr =
@@ -492,7 +526,7 @@
     prev_file_addr = entry.file_addr;
     range_changed = false;
   }
-  if (line_table_up->m_entries.empty())
+  if (line_table_up->get_default_table().empty())
     return nullptr;
   return line_table_up.release();
 }
Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -1163,44 +1163,104 @@
   if (!line_table)
     return false;
 
-  // FIXME: Rather than parsing the whole line table and then copying it over
-  // into LLDB, we should explore using a callback to populate the line table
-  // while we parse to reduce memory usage.
-  std::vector<std::unique_ptr<LineSequence>> sequences;
-  // The Sequences view contains only valid line sequences. Don't iterate over
-  // the Rows directly.
-  for (const llvm::DWARFDebugLine::Sequence &seq : line_table->Sequences) {
-    // Ignore line sequences that do not start after the first code address.
-    // All addresses generated in a sequence are incremental so we only need
-    // to check the first one of the sequence. Check the comment at the
-    // m_first_code_address declaration for more details on this.
-    if (seq.LowPC < m_first_code_address)
-      continue;
-    std::unique_ptr<LineSequence> sequence =
-        LineTable::CreateLineSequenceContainer();
-    for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) {
-      const llvm::DWARFDebugLine::Row &row = line_table->Rows[idx];
-      LineTable::AppendLineEntryToSequence(
-          sequence.get(), row.Address.Address, row.Line, row.Column, row.File,
-          row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin,
-          row.EndSequence);
+  std::unique_ptr<LineTable> line_table_up;
+  if (line_table->isTLLT()) {
+    // TLLT Note: This is experimental / untested!
+    std::vector<std::unique_ptr<LineSequence>> logicals_sequences;
+    std::vector<std::unique_ptr<LineSequence>> actuals_sequences;
+
+    // Logicals:
+    for (const llvm::DWARFDebugLine::Sequence &seq : line_table->Sequences) {
+      // TLLT FIXME: Do we want to do what this comment below says? Or do we
+      // want to ignore addresses, so that we can find all inline contexts?
+      // I think it doesn't matter for setting a breakpoint but will matter for
+      // stepping.
+
+      // Ignore line sequences that do not start after the first code address.
+      // All addresses generated in a sequence are incremental so we only need
+      // to check the first one of the sequence. Check the comment at the
+      // m_first_code_address declaration for more details on this.
+      if (seq.LowPC < m_first_code_address)
+        continue;
+      std::unique_ptr<LineSequence> sequence =
+          LineTable::CreateLineSequenceContainer();
+      for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) {
+        const llvm::DWARFDebugLine::Row &row = line_table->getRows(
+            llvm::DWARFDebugLine::LineTableType::LogicalTable)[idx];
+        LineTable::AppendLineEntryToSequence(
+            sequence.get(), row.Address.Address, row.Line, row.Column, row.File,
+            row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin,
+            row.EndSequence, /*merge_adjacent_equ_file_addr*/ false);
+      }
+      logicals_sequences.push_back(std::move(sequence));
     }
-    sequences.push_back(std::move(sequence));
-  }
 
-  std::unique_ptr<LineTable> line_table_up =
-      std::make_unique<LineTable>(&comp_unit, std::move(sequences));
+    // Actuals:
+    for (const llvm::DWARFDebugLine::Sequence &seq : line_table->Sequences) {
+      // Ignore line sequences that do not start after the first code address.
+      // All addresses generated in a sequence are incremental so we only need
+      // to check the first one of the sequence. Check the comment at the
+      // m_first_code_address declaration for more details on this.
+      if (seq.LowPC < m_first_code_address)
+        continue;
+      std::unique_ptr<LineSequence> sequence =
+          LineTable::CreateLineSequenceContainer();
+      for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) {
+        const llvm::DWARFDebugLine::Row &row = line_table->getRows(
+            llvm::DWARFDebugLine::LineTableType::ActualTable)[idx];
+        LineTable::AppendLineEntryToSequence(
+            sequence.get(), row.Address.Address, row.Line, row.Column, row.File,
+            row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin,
+            row.EndSequence, /*merge_adjacent_equ_file_addr*/ true);
+      }
+      actuals_sequences.push_back(std::move(sequence));
+    }
 
+    line_table_up =
+        std::make_unique<LineTable>(&comp_unit, std::move(logicals_sequences),
+                                    std::move(actuals_sequences));
+  } else {
+    // FIXME: Rather than parsing the whole line table and then copying it over
+    // into LLDB, we should explore using a callback to populate the line table
+    // while we parse to reduce memory usage.
+    std::vector<std::unique_ptr<LineSequence>> sequences;
+    // The Sequences view contains only valid line sequences. Don't iterate over
+    // the Rows directly.
+    for (const llvm::DWARFDebugLine::Sequence &seq : line_table->Sequences) {
+      // Ignore line sequences that do not start after the first code address.
+      // All addresses generated in a sequence are incremental so we only need
+      // to check the first one of the sequence. Check the comment at the
+      // m_first_code_address declaration for more details on this.
+      if (seq.LowPC < m_first_code_address)
+        continue;
+      std::unique_ptr<LineSequence> sequence =
+          LineTable::CreateLineSequenceContainer();
+      for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) {
+        const llvm::DWARFDebugLine::Row &row = line_table->getRows(
+            llvm::DWARFDebugLine::LineTableType::SingleLevelLineTable)[idx];
+        LineTable::AppendLineEntryToSequence(
+            sequence.get(), row.Address.Address, row.Line, row.Column, row.File,
+            row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin,
+            row.EndSequence);
+      }
+      sequences.push_back(std::move(sequence));
+    }
+
+    line_table_up =
+        std::make_unique<LineTable>(&comp_unit, std::move(sequences));
+  }
+
+  assert(line_table_up);
   if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) {
-    // We have an object file that has a line table with addresses that are not
-    // linked. We need to link the line table and convert the addresses that
-    // are relative to the .o file into addresses for the main executable.
+    // We have an object file that has a line table with addresses that are
+    // not linked. We need to link the line table and convert the addresses
+    // that are relative to the .o file into addresses for the main
+    // executable.
     comp_unit.SetLineTable(
         debug_map_symfile->LinkOSOLineTable(this, line_table_up.get()));
   } else {
     comp_unit.SetLineTable(line_table_up.release());
   }
-
   return true;
 }
 
Index: lldb/include/lldb/Symbol/LineTable.h
===================================================================
--- lldb/include/lldb/Symbol/LineTable.h
+++ lldb/include/lldb/Symbol/LineTable.h
@@ -52,6 +52,14 @@
   LineTable(CompileUnit *comp_unit,
             std::vector<std::unique_ptr<LineSequence>> &&sequences);
 
+  /// TLLT: Construct with entries.
+  ///
+  /// \param[in] sequences
+  ///     Unsorted list of line sequences.
+  LineTable(CompileUnit *comp_unit,
+            std::vector<std::unique_ptr<LineSequence>> &&logicals,
+            std::vector<std::unique_ptr<LineSequence>> &&actuals);
+
   /// Destructor.
   ~LineTable();
 
@@ -78,12 +86,13 @@
 
   // Append an entry to a caller-provided collection that will later be
   // inserted in this line table.
-  static void AppendLineEntryToSequence(LineSequence *sequence, lldb::addr_t file_addr,
-                                 uint32_t line, uint16_t column,
-                                 uint16_t file_idx, bool is_start_of_statement,
-                                 bool is_start_of_basic_block,
-                                 bool is_prologue_end, bool is_epilogue_begin,
-                                 bool is_terminal_entry);
+  static void
+  AppendLineEntryToSequence(LineSequence *sequence, lldb::addr_t file_addr,
+                            uint32_t line, uint16_t column, uint16_t file_idx,
+                            bool is_start_of_statement,
+                            bool is_start_of_basic_block, bool is_prologue_end,
+                            bool is_epilogue_begin, bool is_terminal_entry,
+                            bool merge_adjacent_equ_file_addr = true);
 
   // Insert a sequence of entries into this line table.
   void InsertSequence(LineSequence *sequence);
@@ -300,6 +309,9 @@
     /// The file index into CompileUnit's file table, or zero if there
     /// is no file information.
     uint16_t file_idx = 0;
+    /// TLLT Context.
+    uint32_t Context;
+    /// TLLT TODO: FunctionName.
   };
 
 protected:
@@ -320,6 +332,18 @@
   entry_collection
       m_entries; ///< The collection of line entries in this line table.
 
+  entry_collection &get_default_table() {
+    if (m_isTLLT)
+      return m_TLLTLogicals;
+    return m_entries;
+  }
+
+  // TLLT NOTE: Very experimental stuff here.
+  entry_collection m_TLLTLogicals; //< Logicals table entries.
+  entry_collection m_TLLTActuals;  //< Actuals table entries.
+
+  bool m_isTLLT = false;
+
   // Helper class
   class LineSequenceImpl : public LineSequence {
   public:
@@ -344,7 +368,8 @@
       uint32_t start_idx, T file_idx,
       const SourceLocationSpec &src_location_spec, LineEntry *line_entry_ptr,
       std::function<bool(T, uint16_t)> file_idx_matcher) {
-    const size_t count = m_entries.size();
+    auto &table = get_default_table();
+    const size_t count = table.size();
     size_t best_match = UINT32_MAX;
 
     if (!line_entry_ptr)
@@ -358,10 +383,10 @@
     for (size_t idx = start_idx; idx < count; ++idx) {
       // Skip line table rows that terminate the previous row (is_terminal_entry
       // is non-zero)
-      if (m_entries[idx].is_terminal_entry)
+      if (table[idx].is_terminal_entry)
         continue;
 
-      if (!file_idx_matcher(file_idx, m_entries[idx].file_idx))
+      if (!file_idx_matcher(file_idx, table[idx].file_idx))
         continue;
 
       // Exact match always wins.  Otherwise try to find the closest line > the
@@ -370,31 +395,31 @@
       // after and if they're not in the same function, don't return a match.
 
       if (column == LLDB_INVALID_COLUMN_NUMBER) {
-        if (m_entries[idx].line < line) {
+        if (table[idx].line < line) {
           continue;
-        } else if (m_entries[idx].line == line) {
+        } else if (table[idx].line == line) {
           ConvertEntryAtIndexToLineEntry(idx, *line_entry_ptr);
           return idx;
         } else if (!exact_match) {
           if (best_match == UINT32_MAX ||
-              m_entries[idx].line < m_entries[best_match].line)
+              table[idx].line < table[best_match].line)
             best_match = idx;
         }
       } else {
-        if (m_entries[idx].line < line) {
+        if (table[idx].line < line) {
           continue;
-        } else if (m_entries[idx].line == line &&
-                   m_entries[idx].column == column) {
+        } else if (table[idx].line == line &&
+                   table[idx].column == column) {
           ConvertEntryAtIndexToLineEntry(idx, *line_entry_ptr);
           return idx;
         } else if (!exact_match) {
           if (best_match == UINT32_MAX)
             best_match = idx;
-          else if (m_entries[idx].line < m_entries[best_match].line)
+          else if (table[idx].line < table[best_match].line)
             best_match = idx;
-          else if (m_entries[idx].line == m_entries[best_match].line)
-            if (m_entries[idx].column &&
-                m_entries[idx].column < m_entries[best_match].column)
+          else if (table[idx].line == table[best_match].line)
+            if (table[idx].column &&
+                table[idx].column < table[best_match].column)
               best_match = idx;
         }
       }
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -4283,6 +4283,12 @@
                    DebuggerTuning == llvm::DebuggerKind::DBX))
     CmdArgs.push_back("-gstrict-dwarf");
 
+  if (const Arg *A = Args.getLastArg(options::OPT_gtwo_level_line_tables))
+    (void)checkDebugInfoOption(A, Args, D, TC);
+  if (Args.hasFlag(options::OPT_gtwo_level_line_tables,
+                   options::OPT_gnotwo_level_line_tables, false))
+    CmdArgs.push_back("-gtwo-level-line-tables");
+
   // And we handle flag -grecord-gcc-switches later with DWARFDebugFlags.
   Args.ClaimAllArgs(options::OPT_g_flags_Group);
 
Index: clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
===================================================================
--- clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
+++ clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp
@@ -163,6 +163,8 @@
     CodeGenOpts.DebugPrefixMap =
         CI.getInvocation().getCodeGenOpts().DebugPrefixMap;
     CodeGenOpts.DebugStrictDwarf = CI.getCodeGenOpts().DebugStrictDwarf;
+    CodeGenOpts.EnableTwoLevelLineTables =
+        CI.getCodeGenOpts().EnableTwoLevelLineTables;
   }
 
   ~PCHContainerGenerator() override = default;
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -436,6 +436,7 @@
   Options.Hotpatch = CodeGenOpts.HotPatch;
   Options.JMCInstrument = CodeGenOpts.JMCInstrument;
   Options.XCOFFReadOnlyPointers = CodeGenOpts.XCOFFReadOnlyPointers;
+  Options.EnableTwoLevelLineTables = CodeGenOpts.EnableTwoLevelLineTables;
 
   switch (CodeGenOpts.getSwiftAsyncFramePointer()) {
   case CodeGenOptions::SwiftAsyncFramePointerKind::Auto:
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -3305,6 +3305,9 @@
   CodeGenOpts<"NoInlineLineTables">, DefaultFalse,
   NegFlag<SetTrue, [CC1Option], "Don't emit inline line tables.">,
   PosFlag<SetFalse>, BothFlags<[CoreOption]>>;
+defm two_level_line_tables : BoolGOption<"two-level-line-tables",
+  CodeGenOpts<"EnableTwoLevelLineTables">, DefaultFalse,
+  PosFlag<SetTrue, [CC1Option]>, NegFlag<SetFalse>, BothFlags<[CoreOption]>>;
 
 def gfull : Flag<["-"], "gfull">, Group<g_Group>;
 def gused : Flag<["-"], "gused">, Group<g_Group>;
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -182,6 +182,7 @@
 CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled.
 CODEGENOPT(NoInlineLineTables, 1, 0) ///< Whether debug info should contain
                                      ///< inline line tables.
+CODEGENOPT(EnableTwoLevelLineTables, 1, 0) ///< Emit Two Level Line Tables
 CODEGENOPT(StackClashProtector, 1, 0) ///< Set when -fstack-clash-protection is enabled.
 CODEGENOPT(NoImplicitFloat   , 1, 0) ///< Set when -mno-implicit-float is enabled.
 CODEGENOPT(NullPointerIsValid , 1, 0) ///< Assume Null pointer deference is defined.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to