[llvm] fe6cdde - [DWARF] Allow op-index in line number programs

David Stenberg via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 12 04:48:04 PDT 2023


Author: David Stenberg
Date: 2023-07-12T13:46:29+02:00
New Revision: fe6cddef2020c8e103cff8180540d4e2e9102c6d

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

LOG: [DWARF] Allow op-index in line number programs

This extends DWARFDebugLine to properly parse line number programs with
maximum_operations_per_instruction > 1 for VLIW targets.

No functions that use that parsed output to retrieve line information
have been extended to support multiple op-indexes. This means that when
retrieving information for an address with multiple op-indexes, e.g.
when using llvm-addr2line, the penultimate row for that address will be
used, which in most cases is the row for the second largest op-index.
This will be addressed in further changes, but this patch at least
allows us to correctly parse such line number programs, with a warning
saying that the line number information may be incorrect (incomplete).

Reviewed By: StephenTozer

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

Added: 
    

Modified: 
    llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
    llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
    llvm/test/MC/ELF/RISCV/gen-dwarf.s
    llvm/test/tools/llvm-dwarfdump/X86/tombstone.s
    llvm/test/tools/llvm-dwarfdump/X86/verbose.test
    llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
index dc32f1ece18a93..ce3bae6a1760c2 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h
@@ -377,29 +377,37 @@ class DWARFDebugLine {
     void resetRowAndSequence();
     void appendRowToMatrix();
 
-    /// Advance the address by the \p OperationAdvance value. \returns the
-    /// amount advanced by.
-    uint64_t advanceAddr(uint64_t OperationAdvance, uint8_t Opcode,
-                         uint64_t OpcodeOffset);
+    struct AddrOpIndexDelta {
+      uint64_t AddrOffset;
+      int16_t OpIndexDelta;
+    };
+
+    /// Advance the address and op-index by the \p OperationAdvance value.
+    /// \returns the amount advanced by.
+    AddrOpIndexDelta advanceAddrOpIndex(uint64_t OperationAdvance,
+                                        uint8_t Opcode, uint64_t OpcodeOffset);
 
-    struct AddrAndAdjustedOpcode {
+    struct OpcodeAdvanceResults {
       uint64_t AddrDelta;
+      int16_t OpIndexDelta;
       uint8_t AdjustedOpcode;
     };
 
-    /// Advance the address as required by the specified \p Opcode.
+    /// Advance the address and op-index as required by the specified \p Opcode.
     /// \returns the amount advanced by and the calculated adjusted opcode.
-    AddrAndAdjustedOpcode advanceAddrForOpcode(uint8_t Opcode,
-                                               uint64_t OpcodeOffset);
+    OpcodeAdvanceResults advanceForOpcode(uint8_t Opcode,
+                                          uint64_t OpcodeOffset);
 
-    struct AddrAndLineDelta {
+    struct SpecialOpcodeDelta {
       uint64_t Address;
       int32_t Line;
+      int16_t OpIndex;
     };
 
-    /// Advance the line and address as required by the specified special \p
-    /// Opcode. \returns the address and line delta.
-    AddrAndLineDelta handleSpecialOpcode(uint8_t Opcode, uint64_t OpcodeOffset);
+    /// Advance the line, address and op-index as required by the specified
+    /// special \p Opcode. \returns the address, op-index and line delta.
+    SpecialOpcodeDelta handleSpecialOpcode(uint8_t Opcode,
+                                           uint64_t OpcodeOffset);
 
     /// Line table we're currently parsing.
     struct LineTable *LineTable;

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
index 818cc25db60dbf..6f2afe5d50e9c8 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
@@ -610,21 +610,36 @@ static StringRef getOpcodeName(uint8_t Opcode, uint8_t OpcodeBase) {
   return "special";
 }
 
-uint64_t DWARFDebugLine::ParsingState::advanceAddr(uint64_t OperationAdvance,
-                                                   uint8_t Opcode,
-                                                   uint64_t OpcodeOffset) {
+DWARFDebugLine::ParsingState::AddrOpIndexDelta
+DWARFDebugLine::ParsingState::advanceAddrOpIndex(uint64_t OperationAdvance,
+                                                 uint8_t Opcode,
+                                                 uint64_t OpcodeOffset) {
   StringRef OpcodeName = getOpcodeName(Opcode, LineTable->Prologue.OpcodeBase);
   // For versions less than 4, the MaxOpsPerInst member is set to 0, as the
   // maximum_operations_per_instruction field wasn't introduced until DWARFv4.
   // Don't warn about bad values in this situation.
   if (ReportAdvanceAddrProblem && LineTable->Prologue.getVersion() >= 4 &&
-      LineTable->Prologue.MaxOpsPerInst != 1)
+      LineTable->Prologue.MaxOpsPerInst == 0)
+    ErrorHandler(createStringError(
+        errc::invalid_argument,
+        "line table program at offset 0x%8.8" PRIx64
+        " contains a %s opcode at offset 0x%8.8" PRIx64
+        ", but the prologue maximum_operations_per_instruction value is 0"
+        ", which is invalid. Assuming a value of 1 instead",
+        LineTableOffset, OpcodeName.data(), OpcodeOffset));
+  // Although we are able to correctly parse line number programs with
+  // MaxOpsPerInst > 1, the rest of DWARFDebugLine and its
+  // users have not been updated to handle line information for all operations
+  // in a multi-operation instruction, so warn about potentially incorrect
+  // results.
+  if (ReportAdvanceAddrProblem && LineTable->Prologue.MaxOpsPerInst > 1)
     ErrorHandler(createStringError(
         errc::not_supported,
         "line table program at offset 0x%8.8" PRIx64
         " contains a %s opcode at offset 0x%8.8" PRIx64
         ", but the prologue maximum_operations_per_instruction value is %" PRId8
-        ", which is unsupported. Assuming a value of 1 instead",
+        ", which is experimentally supported, so line number information "
+        "may be incorrect",
         LineTableOffset, OpcodeName.data(), OpcodeOffset,
         LineTable->Prologue.MaxOpsPerInst));
   if (ReportAdvanceAddrProblem && LineTable->Prologue.MinInstLength == 0)
@@ -636,14 +651,35 @@ uint64_t DWARFDebugLine::ParsingState::advanceAddr(uint64_t OperationAdvance,
                           "is 0, which prevents any address advancing",
                           LineTableOffset, OpcodeName.data(), OpcodeOffset));
   ReportAdvanceAddrProblem = false;
-  uint64_t AddrOffset = OperationAdvance * LineTable->Prologue.MinInstLength;
+
+  // Advances the address and op_index according to DWARFv5, section 6.2.5.1:
+  //
+  // new address = address +
+  //   minimum_instruction_length *
+  //   ((op_index + operation advance) / maximum_operations_per_instruction)
+  //
+  // new op_index =
+  //   (op_index + operation advance) % maximum_operations_per_instruction
+
+  // For versions less than 4, the MaxOpsPerInst member is set to 0, as the
+  // maximum_operations_per_instruction field wasn't introduced until DWARFv4.
+  uint8_t MaxOpsPerInst =
+      std::max(LineTable->Prologue.MaxOpsPerInst, uint8_t{1});
+
+  uint64_t AddrOffset = ((Row.OpIndex + OperationAdvance) / MaxOpsPerInst) *
+                        LineTable->Prologue.MinInstLength;
   Row.Address.Address += AddrOffset;
-  return AddrOffset;
+
+  uint8_t PrevOpIndex = Row.OpIndex;
+  Row.OpIndex = (Row.OpIndex + OperationAdvance) % MaxOpsPerInst;
+  int16_t OpIndexDelta = static_cast<int16_t>(Row.OpIndex) - PrevOpIndex;
+
+  return {AddrOffset, OpIndexDelta};
 }
 
-DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode
-DWARFDebugLine::ParsingState::advanceAddrForOpcode(uint8_t Opcode,
-                                                   uint64_t OpcodeOffset) {
+DWARFDebugLine::ParsingState::OpcodeAdvanceResults
+DWARFDebugLine::ParsingState::advanceForOpcode(uint8_t Opcode,
+                                               uint64_t OpcodeOffset) {
   assert(Opcode == DW_LNS_const_add_pc ||
          Opcode >= LineTable->Prologue.OpcodeBase);
   if (ReportBadLineRange && LineTable->Prologue.LineRange == 0) {
@@ -667,11 +703,12 @@ DWARFDebugLine::ParsingState::advanceAddrForOpcode(uint8_t Opcode,
       LineTable->Prologue.LineRange != 0
           ? AdjustedOpcode / LineTable->Prologue.LineRange
           : 0;
-  uint64_t AddrOffset = advanceAddr(OperationAdvance, Opcode, OpcodeOffset);
-  return {AddrOffset, AdjustedOpcode};
+  AddrOpIndexDelta Advance =
+      advanceAddrOpIndex(OperationAdvance, Opcode, OpcodeOffset);
+  return {Advance.AddrOffset, Advance.OpIndexDelta, AdjustedOpcode};
 }
 
-DWARFDebugLine::ParsingState::AddrAndLineDelta
+DWARFDebugLine::ParsingState::SpecialOpcodeDelta
 DWARFDebugLine::ParsingState::handleSpecialOpcode(uint8_t Opcode,
                                                   uint64_t OpcodeOffset) {
   // A special opcode value is chosen based on the amount that needs
@@ -705,15 +742,16 @@ DWARFDebugLine::ParsingState::handleSpecialOpcode(uint8_t Opcode,
   //
   // line increment = line_base + (adjusted opcode % line_range)
 
-  DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode AddrAdvanceResult =
-      advanceAddrForOpcode(Opcode, OpcodeOffset);
+  DWARFDebugLine::ParsingState::OpcodeAdvanceResults AddrAdvanceResult =
+      advanceForOpcode(Opcode, OpcodeOffset);
   int32_t LineOffset = 0;
   if (LineTable->Prologue.LineRange != 0)
     LineOffset =
         LineTable->Prologue.LineBase +
         (AddrAdvanceResult.AdjustedOpcode % LineTable->Prologue.LineRange);
   Row.Line += LineOffset;
-  return {AddrAdvanceResult.AddrDelta, LineOffset};
+  return {AddrAdvanceResult.AddrDelta, LineOffset,
+          AddrAdvanceResult.OpIndexDelta};
 }
 
 /// Parse a ULEB128 using the specified \p Cursor. \returns the parsed value on
@@ -860,9 +898,10 @@ Error DWARFDebugLine::LineTable::parse(
         // Takes a single relocatable address as an operand. The size of the
         // operand is the size appropriate to hold an address on the target
         // machine. Set the address register to the value given by the
-        // relocatable address. All of the other statement program opcodes
-        // that affect the address register add a delta to it. This instruction
-        // stores a relocatable value into it instead.
+        // relocatable address and set the op_index register to 0. All of the
+        // other statement program opcodes that affect the address register
+        // add a delta to it. This instruction stores a relocatable value into
+        // it instead.
         //
         // Make sure the extractor knows the address size.  If not, infer it
         // from the size of the operand.
@@ -893,6 +932,7 @@ Error DWARFDebugLine::LineTable::parse(
             TableData.setAddressSize(OpcodeAddressSize);
             State.Row.Address.Address = TableData.getRelocatedAddress(
                 Cursor, &State.Row.Address.SectionIndex);
+            State.Row.OpIndex = 0;
 
             uint64_t Tombstone =
                 dwarf::computeTombstoneAddress(OpcodeAddressSize);
@@ -1004,15 +1044,16 @@ Error DWARFDebugLine::LineTable::parse(
         break;
 
       case DW_LNS_advance_pc:
-        // Takes a single unsigned LEB128 operand, multiplies it by the
-        // min_inst_length field of the prologue, and adds the
-        // result to the address register of the state machine.
+        // Takes a single unsigned LEB128 operand as the operation advance
+        // and modifies the address and op_index registers of the state machine
+        // according to that.
         if (std::optional<uint64_t> Operand =
                 parseULEB128<uint64_t>(TableData, Cursor)) {
-          uint64_t AddrOffset =
-              State.advanceAddr(*Operand, Opcode, OpcodeOffset);
+          ParsingState::AddrOpIndexDelta Advance =
+              State.advanceAddrOpIndex(*Operand, Opcode, OpcodeOffset);
           if (Verbose)
-            *OS << " (" << AddrOffset << ")";
+            *OS << " (addr += " << Advance.AddrOffset
+                << ", op-index += " << Advance.OpIndexDelta << ")";
         }
         break;
 
@@ -1064,8 +1105,8 @@ Error DWARFDebugLine::LineTable::parse(
         break;
 
       case DW_LNS_const_add_pc:
-        // Takes no arguments. Add to the address register of the state
-        // machine the address increment value corresponding to special
+        // Takes no arguments. Advance the address and op_index registers of
+        // the state machine by the increments corresponding to special
         // opcode 255. The motivation for DW_LNS_const_add_pc is this:
         // when the statement program needs to advance the address by a
         // small amount, it can use a single special opcode, which occupies
@@ -1076,30 +1117,35 @@ Error DWARFDebugLine::LineTable::parse(
         // than twice that range will it need to use both DW_LNS_advance_pc
         // and a special opcode, requiring three or more bytes.
         {
-          uint64_t AddrOffset =
-              State.advanceAddrForOpcode(Opcode, OpcodeOffset).AddrDelta;
+          ParsingState::OpcodeAdvanceResults Advance =
+              State.advanceForOpcode(Opcode, OpcodeOffset);
           if (Verbose)
-            *OS << format(" (0x%16.16" PRIx64 ")", AddrOffset);
+            *OS << format(" (addr += 0x%16.16" PRIx64 ", op-index += %" PRIu8
+                          ")",
+                          Advance.AddrDelta, Advance.OpIndexDelta);
         }
         break;
 
       case DW_LNS_fixed_advance_pc:
         // Takes a single uhalf operand. Add to the address register of
-        // the state machine the value of the (unencoded) operand. This
-        // is the only extended opcode that takes an argument that is not
-        // a variable length number. The motivation for DW_LNS_fixed_advance_pc
-        // is this: existing assemblers cannot emit DW_LNS_advance_pc or
-        // special opcodes because they cannot encode LEB128 numbers or
-        // judge when the computation of a special opcode overflows and
-        // requires the use of DW_LNS_advance_pc. Such assemblers, however,
-        // can use DW_LNS_fixed_advance_pc instead, sacrificing compression.
+        // the state machine the value of the (unencoded) operand and set
+        // the op_index register to 0. This is the only extended opcode that
+        // takes an argument that is not a variable length number.
+        // The motivation for DW_LNS_fixed_advance_pc is this: existing
+        // assemblers cannot emit DW_LNS_advance_pc or special opcodes because
+        // they cannot encode LEB128 numbers or judge when the computation
+        // of a special opcode overflows and requires the use of
+        // DW_LNS_advance_pc. Such assemblers, however, can use
+        // DW_LNS_fixed_advance_pc instead, sacrificing compression.
         {
           uint16_t PCOffset =
               TableData.getRelocatedValue(Cursor, 2);
           if (Cursor) {
             State.Row.Address.Address += PCOffset;
+            State.Row.OpIndex = 0;
             if (Verbose)
-              *OS << format(" (0x%4.4" PRIx16 ")", PCOffset);
+              *OS << format(" (addr += 0x%4.4" PRIx16 ", op-index = 0)",
+                            PCOffset);
           }
         }
         break;
@@ -1163,11 +1209,12 @@ Error DWARFDebugLine::LineTable::parse(
       *OffsetPtr = Cursor.tell();
     } else {
       // Special Opcodes.
-      ParsingState::AddrAndLineDelta Delta =
+      ParsingState::SpecialOpcodeDelta Delta =
           State.handleSpecialOpcode(Opcode, OpcodeOffset);
 
       if (Verbose)
-        *OS << "address += " << Delta.Address << ",  line += " << Delta.Line;
+        *OS << "address += " << Delta.Address << ",  line += " << Delta.Line
+            << ",  op-index += " << Delta.OpIndex;
       EmitRow();
       *OffsetPtr = Cursor.tell();
     }
@@ -1228,6 +1275,9 @@ uint32_t DWARFDebugLine::LineTable::findRowInSeq(
   //
   // In general we want a non-empty range: the last row whose address is less
   // than or equal to Address. This can be computed as upper_bound - 1.
+  //
+  // TODO: This function, and its users, needs to be update to return multiple
+  // rows for bundles with multiple op-indexes.
   DWARFDebugLine::Row Row;
   Row.Address = Address;
   RowIter FirstRow = Rows.begin() + Seq.FirstRowIndex;

diff  --git a/llvm/test/MC/ELF/RISCV/gen-dwarf.s b/llvm/test/MC/ELF/RISCV/gen-dwarf.s
index c0c8cae61c72ba..670f833f258d25 100644
--- a/llvm/test/MC/ELF/RISCV/gen-dwarf.s
+++ b/llvm/test/MC/ELF/RISCV/gen-dwarf.s
@@ -26,11 +26,11 @@
 # CHECK-NEXT: DW_LNS_copy
 # CHECK-NEXT:                           is_stmt
 # CHECK-NEXT: DW_LNS_advance_line
-# CHECK-NEXT: DW_LNS_fixed_advance_pc (0x0004)
+# CHECK-NEXT: DW_LNS_fixed_advance_pc (addr += 0x0004, op-index = 0)
 # CHECK-NEXT: DW_LNS_copy
 # CHECK-NEXT:                           is_stmt
 # CHECK-NEXT: DW_LNS_advance_line
-# CHECK-NEXT: DW_LNS_fixed_advance_pc (0x0004)
+# CHECK-NEXT: DW_LNS_fixed_advance_pc (addr += 0x0004, op-index = 0)
 # CHECK-NEXT: DW_LNS_copy
 
 # CHECK:      0x00000000: range list header: length = 0x0000001d, format = DWARF32, version = 0x0005

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/tombstone.s b/llvm/test/tools/llvm-dwarfdump/X86/tombstone.s
index 7b4ff70e5ff584..d72a6cbc17725f 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/tombstone.s
+++ b/llvm/test/tools/llvm-dwarfdump/X86/tombstone.s
@@ -63,23 +63,23 @@
 # CHECK-NEXT: --------------
 # CHECK-NEXT: DW_LNE_set_address (0xffffffff)
 # CHECK-NEXT: DW_LNS_copy
-# CHECK-NEXT: DW_LNS_advance_pc (1)
+# CHECK-NEXT: DW_LNS_advance_pc (addr += 1, op-index += 0)
 # CHECK-NEXT: DW_LNE_end_sequence
 # CHECK-NEXT: DW_LNE_set_address (0x00000042)
 # CHECK-NEXT: DW_LNS_copy
 # CHECK-NEXT:   0x0000000000000042 1
-# CHECK-NEXT: DW_LNS_advance_pc (1)
+# CHECK-NEXT: DW_LNS_advance_pc (addr += 1, op-index += 0)
 # CHECK-NEXT: DW_LNE_end_sequence
 # CHECK:      Address Line
 # CHECK-NEXT: --------------
 # CHECK-NEXT: DW_LNE_set_address (0xffffffffffffffff)
 # CHECK-NEXT: DW_LNS_copy
-# CHECK-NEXT: DW_LNS_advance_pc (1)
+# CHECK-NEXT: DW_LNS_advance_pc (addr += 1, op-index += 0)
 # CHECK-NEXT: DW_LNE_end_sequence
 # CHECK-NEXT: DW_LNE_set_address (0x0000000000000042)
 # CHECK-NEXT: DW_LNS_copy
 # CHECK-NEXT:   0x0000000000000042 1
-# CHECK-NEXT: DW_LNS_advance_pc (1)
+# CHECK-NEXT: DW_LNS_advance_pc (addr += 1, op-index += 0)
 # CHECK-NEXT: DW_LNE_end_sequence
 
 # Dumping of the debug_addr, ranges, and rnglists sections don't do anything

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/verbose.test b/llvm/test/tools/llvm-dwarfdump/X86/verbose.test
index 0e74fc7a624e7f..f4513cf35c4d34 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/verbose.test
+++ b/llvm/test/tools/llvm-dwarfdump/X86/verbose.test
@@ -22,9 +22,9 @@
 #  CHECK-NEXT:            0x0000000000000000      1      0      1   0             0       0  is_stmt
 #  CHECK-NEXT:0x00000035: 05 DW_LNS_set_column (12)
 #  CHECK-NEXT:0x00000037: 0a DW_LNS_set_prologue_end
-#  CHECK-NEXT:0x00000038: 66 address += 6,  line += 0
+#  CHECK-NEXT:0x00000038: 66 address += 6,  line += 0,  op-index += 0
 #  CHECK-NEXT:            0x0000000000000006      1     12      1   0             0       0  is_stmt prologue_end
-#  CHECK-NEXT:0x00000039: 02 DW_LNS_advance_pc (2)
+#  CHECK-NEXT:0x00000039: 02 DW_LNS_advance_pc (addr += 2, op-index += 0)
 #  CHECK-NEXT:0x0000003b: 00 DW_LNE_end_sequence
 #  CHECK-NEXT:            0x0000000000000008      1     12      1   0             0       0  is_stmt end_sequence
 # CHECK-EMPTY:

diff  --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
index 871186700f07aa..9635011601cd25 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
@@ -1085,41 +1085,201 @@ struct AdjustAddressFixtureBase : public CommonFixture {
   bool IsErrorExpected;
 };
 
-struct MaxOpsPerInstFixture
-    : TestWithParam<std::tuple<uint16_t, uint8_t, bool>>,
-      AdjustAddressFixtureBase {
-  void SetUp() override {
-    std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam();
-  }
-
-  uint64_t editPrologue(LineTable &LT) override {
+struct OpIndexFixture : Test, CommonFixture {
+  void createPrologue(LineTable &LT, uint8_t MaxOpsPerInst,
+                      uint8_t MinInstLength) {
     DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
     Prologue.MaxOpsPerInst = MaxOpsPerInst;
+    Prologue.MinInstLength = MinInstLength;
     LT.setPrologue(Prologue);
-    return Prologue.TotalLength + Prologue.sizeofTotalLength();
   }
-
-  uint8_t MaxOpsPerInst;
 };
 
 #ifdef _AIX
-TEST_P(MaxOpsPerInstFixture, DISABLED_MaxOpsPerInstProblemsReportedCorrectly) {
+TEST_F(OpIndexFixture, DISABLED_OpIndexAdvance) {
 #else
-TEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) {
+TEST_F(OpIndexFixture, OpIndexAdvance) {
 #endif
-  runTest(/*CheckAdvancePC=*/true,
-          "but the prologue maximum_operations_per_instruction value is " +
-              Twine(unsigned(MaxOpsPerInst)) +
-              ", which is unsupported. Assuming a value of 1 instead");
+  if (!setupGenerator(4, 4))
+    GTEST_SKIP();
+
+  uint8_t MaxOpsPerInst = 13;
+  uint8_t MinInstLength = 4;
+
+  LineTable &LT = Gen->addLineTable();
+
+  // Row 0-2: Different locations for one bundle set up using special opcodes.
+  LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x20, LineTable::Long}});
+  LT.addByte(0x13); // Special opcode, +1 line.
+  LT.addByte(0x23); // Special opcode, +3 line, +1 op-index.
+  LT.addByte(0x3a); // Special opcode, -2 line, +3 op-index.
+
+  // Row 3: New bundle, set up using DW_LNS_advance pc.
+  // Operation advance 0x84, which gives +40 addr, +2 op-index
+  LT.addStandardOpcode(DW_LNS_advance_line, {{100, LineTable::SLEB}});
+  LT.addStandardOpcode(DW_LNS_advance_pc, {{0x84, LineTable::ULEB}});
+  LT.addStandardOpcode(DW_LNS_copy, {}); // Create new row.
+
+  // Row 4: New bundle, set up using a single special opcode.
+  LT.addByte(0x71); // Special opcode, +4 addr, -3 line, -6 op-index.
+
+  // Row 5: New bundle, set up using using DW_LNS_const_add_pc.
+  // Corresponds to advancing address and op-index using special opcode 255,
+  // which gives +4 addr, +4 op-index.
+  LT.addStandardOpcode(DW_LNS_advance_line, {{10, LineTable::SLEB}});
+  LT.addStandardOpcode(DW_LNS_const_add_pc, {});
+  LT.addStandardOpcode(DW_LNS_copy, {}); // Create new row.
+
+  // Row 6: End sequence to have the input well-formed.
+  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+  createPrologue(LT, MaxOpsPerInst, MinInstLength);
+
+  generate();
+
+  auto VerifyRow = [](const DWARFDebugLine::Row &Row, uint64_t Address,
+                      uint8_t OpIndex, uint32_t Line) {
+    EXPECT_EQ(Row.Address.Address, Address);
+    EXPECT_EQ(Row.OpIndex, OpIndex);
+    EXPECT_EQ(Row.Line, Line);
+  };
+
+  auto Table = Line.getOrParseLineTable(LineData, 0, *Context, nullptr,
+                                        RecordRecoverable);
+  EXPECT_THAT_ERROR(
+      std::move(Recoverable),
+      FailedWithMessage("line table program at offset 0x00000000 contains a "
+                        "special opcode at offset 0x00000035, but the prologue "
+                        "maximum_operations_per_instruction value is 13, which "
+                        "is experimentally supported, so line number "
+                        "information may be incorrect"));
+  EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
+  ASSERT_THAT_EXPECTED(Table, Succeeded());
+
+  ASSERT_EQ((*Table)->Rows.size(), 7u);
+
+  VerifyRow((*Table)->Rows[0], 0x20, 0, 2);
+  VerifyRow((*Table)->Rows[1], 0x20, 1, 5);
+  VerifyRow((*Table)->Rows[2], 0x20, 4, 3);
+  VerifyRow((*Table)->Rows[3], 0x48, 6, 103);
+  VerifyRow((*Table)->Rows[4], 0x4c, 0, 100);
+  VerifyRow((*Table)->Rows[5], 0x50, 4, 110);
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    MaxOpsPerInstParams, MaxOpsPerInstFixture,
-    Values(std::make_tuple(3, 0, false), // Test for version < 4 (no error).
-           std::make_tuple(4, 0, true),  // Test zero value for V4 (error).
-           std::make_tuple(4, 1, false), // Test good value for V4 (no error).
-           std::make_tuple(
-               4, 2, true))); // Test one higher than permitted V4 (error).
+#ifdef _AIX
+TEST_F(OpIndexFixture, DISABLED_OpIndexReset) {
+#else
+TEST_F(OpIndexFixture, OpIndexReset) {
+#endif
+  if (!setupGenerator(4, 4))
+    GTEST_SKIP();
+
+  uint8_t MaxOpsPerInst = 13;
+  uint8_t MinInstLength = 4;
+
+  LineTable &LT = Gen->addLineTable();
+
+  // Row 0: Just set op-index to some value > 0.
+  LT.addExtendedOpcode(5, DW_LNE_set_address, {{0, LineTable::Long}});
+  LT.addByte(0x20); // Special opcode, +1 op-index
+
+  // Row 1: DW_LNE_fixed_advance_pc should set op-index to 0.
+  LT.addStandardOpcode(DW_LNS_fixed_advance_pc, {{10, LineTable::Half}});
+  LT.addStandardOpcode(DW_LNS_copy, {}); // Create new row.
+
+  // Row 2: Just set op-index to some value > 0.
+  LT.addByte(0x66); // Special opcode, +6 op-index
+
+  // Row 3: DW_LNE_set_address should set op-index to 0.
+  LT.addExtendedOpcode(5, DW_LNE_set_address, {{20, LineTable::Long}});
+  LT.addStandardOpcode(DW_LNS_copy, {}); // Create new row.
+
+  // Row 4: Just set op-index to some value > 0.
+  LT.addByte(0xba); // Special opcode, +12 op-index
+
+  // Row 5: End sequence (op-index unchanged for this row)...
+  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+  // Row 6: ... but shall be reset after the DW_LNE_end_sequence row.
+  LT.addStandardOpcode(DW_LNS_copy, {}); // Create new row.
+
+  // Row 7: End sequence to have the input well-formed.
+  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+  createPrologue(LT, MaxOpsPerInst, MinInstLength);
+
+  generate();
+
+  auto Table = Line.getOrParseLineTable(LineData, 0, *Context, nullptr,
+                                        RecordRecoverable);
+  EXPECT_THAT_ERROR(
+      std::move(Recoverable),
+      FailedWithMessage("line table program at offset 0x00000000 contains a "
+                        "special opcode at offset 0x00000035, but the prologue "
+                        "maximum_operations_per_instruction value is 13, which "
+                        "is experimentally supported, so line number "
+                        "information may be incorrect"));
+  EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
+  ASSERT_THAT_EXPECTED(Table, Succeeded());
+
+  ASSERT_EQ((*Table)->Rows.size(), 8u);
+  EXPECT_EQ((*Table)->Rows[0].OpIndex, 1u);
+  EXPECT_EQ((*Table)->Rows[1].OpIndex, 0u); // DW_LNS_fixed_advance_pc.
+  EXPECT_EQ((*Table)->Rows[2].OpIndex, 6u);
+  EXPECT_EQ((*Table)->Rows[3].OpIndex, 0u); // DW_LNE_set_address.
+  EXPECT_EQ((*Table)->Rows[4].OpIndex, 12u);
+  EXPECT_EQ((*Table)->Rows[5].OpIndex, 12u);
+  EXPECT_EQ((*Table)->Rows[6].OpIndex, 0u); // row after DW_LNE_end_sequence.
+  EXPECT_EQ((*Table)->Rows[7].OpIndex, 0u);
+}
+
+#ifdef _AIX
+TEST_F(OpIndexFixture, DISABLED_MaxOpsZeroDwarf3) {
+#else
+TEST_F(OpIndexFixture, MaxOpsZeroDwarf3) {
+#endif
+  if (!setupGenerator(3, 4))
+    GTEST_SKIP();
+
+  LineTable &LT = Gen->addLineTable();
+  LT.addStandardOpcode(DW_LNS_const_add_pc, {}); // Just some opcode to advance.
+  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); //
+  createPrologue(LT, /*MaxOpsPerInst=*/0, /*MinInstLength=*/1);
+  generate();
+
+  auto Table = Line.getOrParseLineTable(LineData, 0, *Context, nullptr,
+                                        RecordRecoverable);
+  EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
+  EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
+  ASSERT_THAT_EXPECTED(Table, Succeeded());
+}
+
+#ifdef _AIX
+TEST_F(OpIndexFixture, DISABLED_MaxOpsZeroDwarf4) {
+#else
+TEST_F(OpIndexFixture, MaxOpsZeroDwarf4) {
+#endif
+  if (!setupGenerator(4, 4))
+    GTEST_SKIP();
+
+  LineTable &LT = Gen->addLineTable();
+  LT.addStandardOpcode(DW_LNS_const_add_pc, {}); // Just some opcode to advance.
+  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); //
+  createPrologue(LT, /*MaxOpsPerInst=*/0, /*MinInstLength=*/1);
+  generate();
+
+  auto Table = Line.getOrParseLineTable(LineData, 0, *Context, nullptr,
+                                        RecordRecoverable);
+  EXPECT_THAT_ERROR(
+      std::move(Recoverable),
+      FailedWithMessage(
+          "line table program at offset 0x00000000 contains a "
+          "DW_LNS_const_add_pc opcode at offset 0x0000002e, but "
+          "the prologue maximum_operations_per_instruction value "
+          "is 0, which is invalid. Assuming a value of 1 instead"));
+  EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
+  ASSERT_THAT_EXPECTED(Table, Succeeded());
+}
 
 struct LineRangeFixture : TestWithParam<std::tuple<uint8_t, bool>>,
                           AdjustAddressFixtureBase {
@@ -1572,22 +1732,26 @@ TEST_F(DebugLineBasicFixture, VerboseOutput) {
   EXPECT_EQ(NextLine(), "0x00000059: 01 DW_LNS_copy");
   EXPECT_EQ(NextLine(), "            0x0123456789abcdef      1      0      1   "
                         "0           127       0  is_stmt");
-  EXPECT_EQ(NextLine(), "0x0000005a: 02 DW_LNS_advance_pc (11)");
+  EXPECT_EQ(NextLine(), "0x0000005a: 02 DW_LNS_advance_pc (addr += 11, "
+                        "op-index += 0)");
   EXPECT_EQ(NextLine(), "0x0000005c: 03 DW_LNS_advance_line (23)");
   EXPECT_EQ(NextLine(), "0x0000005e: 04 DW_LNS_set_file (33)");
   EXPECT_EQ(NextLine(), "0x00000060: 05 DW_LNS_set_column (44)");
   EXPECT_EQ(NextLine(), "0x00000062: 06 DW_LNS_negate_stmt");
   EXPECT_EQ(NextLine(), "0x00000063: 07 DW_LNS_set_basic_block");
   EXPECT_EQ(NextLine(),
-            "0x00000064: 08 DW_LNS_const_add_pc (0x0000000000000011)");
-  EXPECT_EQ(NextLine(), "0x00000065: 09 DW_LNS_fixed_advance_pc (0x0037)");
+            "0x00000064: 08 DW_LNS_const_add_pc (addr += 0x0000000000000011, "
+            "op-index += 0)");
+  EXPECT_EQ(NextLine(), "0x00000065: 09 DW_LNS_fixed_advance_pc (addr += 0x0037"
+            ", op-index = 0)");
   EXPECT_EQ(NextLine(), "0x00000068: 0a DW_LNS_set_prologue_end");
   EXPECT_EQ(NextLine(), "0x00000069: 0b DW_LNS_set_epilogue_begin");
   EXPECT_EQ(NextLine(), "0x0000006a: 0c DW_LNS_set_isa (66)");
   EXPECT_EQ(NextLine(), "0x0000006c: 0d Unrecognized standard opcode "
                         "(operands: 0x0000000000000001, 0x0123456789abcdef)");
   EXPECT_EQ(NextLine(), "0x00000077: 0e Unrecognized standard opcode");
-  EXPECT_EQ(NextLine(), "0x00000078: ff address += 17,  line += -3");
+  EXPECT_EQ(NextLine(), "0x00000078: ff address += 17,  line += -3,  "
+                        "op-index += 0");
   EXPECT_EQ(NextLine(),
             "            0x0123456789abce53     20     44     33  66           "
             "  0       0  basic_block prologue_end epilogue_begin");


        


More information about the llvm-commits mailing list