diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/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. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3305,6 +3305,9 @@ CodeGenOpts<"NoInlineLineTables">, DefaultFalse, NegFlag, PosFlag, BothFlags<[CoreOption]>>; +defm two_level_line_tables : BoolGOption<"two-level-line-tables", + CodeGenOpts<"EnableTwoLevelLineTables">, DefaultFalse, + PosFlag, NegFlag, BothFlags<[CoreOption]>>; def gfull : Flag<["-"], "gfull">, Group; def gused : Flag<["-"], "gused">, Group; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/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: diff --git a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp --- a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/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; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/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); diff --git a/lldb/include/lldb/Symbol/LineTable.h b/lldb/include/lldb/Symbol/LineTable.h --- a/lldb/include/lldb/Symbol/LineTable.h +++ b/lldb/include/lldb/Symbol/LineTable.h @@ -52,6 +52,14 @@ LineTable(CompileUnit *comp_unit, std::vector> &&sequences); + /// TLLT: Construct with entries. + /// + /// \param[in] sequences + /// Unsorted list of line sequences. + LineTable(CompileUnit *comp_unit, + std::vector> &&logicals, + std::vector> &&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 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; } } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/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> 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 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 line_table_up; + if (line_table->isTLLT()) { + // TLLT Note: This is experimental / untested! + std::vector> logicals_sequences; + std::vector> 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 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 line_table_up = - std::make_unique(&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 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(&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> 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 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(&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; } diff --git a/lldb/source/Symbol/LineTable.cpp b/lldb/source/Symbol/LineTable.cpp --- a/lldb/source/Symbol/LineTable.cpp +++ b/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(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> &&logicals, + std::vector> &&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(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(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(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 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(); } diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/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) diff --git a/llvm/include/llvm/CodeGen/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h --- a/llvm/include/llvm/CodeGen/CommandFlags.h +++ b/llvm/include/llvm/CodeGen/CommandFlags.h @@ -131,6 +131,8 @@ bool getEnableDebugEntryValues(); +bool getEnableTwoLevelLineTables(); + bool getValueTrackingVariableLocations(); std::optional getExplicitValueTrackingVariableLocations(); diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/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; 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 ErrorHandler); void resetRowAndSequence(); - void appendRowToMatrix(); + void appendRowToMatrix(LineTableType RowType); /// Advance the address by the \p OperationAdvance value. \returns the /// amount advanced by. diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h +++ b/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; diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -205,6 +205,11 @@ MCDwarfLoc CurrentDwarfLoc; bool DwarfLocSeen = false; + SmallVector 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 &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; } diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/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 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; -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 { + 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 Checksum, std::optional Source, uint16_t DwarfVersion, unsigned FileNumber = 0); - std::pair + std::tuple Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, std::optional &LineStr) const; - std::pair + std::tuple Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, ArrayRef SpecialOpcodeLengths, std::optional &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 &LineStr); Expected tryGetFile(StringRef &Directory, StringRef &FileName, std::optional Checksum, diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/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. diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/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. diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/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 LogicalsLookup; + /// TLLT: Look-up map for inlined call site logical locations. + DenseMap 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; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/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(ScopeMD)) { + Data.FileName = Scope->getFilename(); + if (Line != 0 && DwarfVersion >= 4) + if (auto *LBF = dyn_cast(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(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> DCUs) { - StringRef Fn; - unsigned FileNo = 1; - unsigned Discriminator = 0; - if (auto *Scope = cast_or_null(S)) { - Fn = Scope->getFilename(); - if (Line != 0 && DwarfVersion >= 4) - if (auto *LBF = dyn_cast(Scope)) - Discriminator = LBF->getDiscriminator(); - - FileNo = static_cast(*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> 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(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) diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp --- a/llvm/lib/CodeGen/CommandFlags.cpp +++ b/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 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 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(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/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); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/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 Context = + parseULEB128(TableData, Cursor)) { + State.Row.Context = *Context; + if (Verbose) + *OS << " (" << (uint64_t)State.Row.Context << ")"; + } + if (std::optional SubprogramOffset = + parseULEB128(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; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/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'; } diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp --- a/llvm/lib/MC/MCContext.cpp +++ b/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 : ""; diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp --- a/llvm/lib/MC/MCDwarf.cpp +++ b/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 &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 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 +std::tuple MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, std::optional &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 +std::tuple MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, ArrayRef StandardOpcodeLengths, std::optional &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 &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). diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp --- a/llvm/lib/MC/MCStreamer.cpp +++ b/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()) {