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 @@ -377,29 +377,37 @@ 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 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -610,21 +610,36 @@ 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 @@ "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(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 @@ 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 @@ // // 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 @@ // 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 @@ 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 @@ 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 Operand = parseULEB128(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 @@ 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 @@ // 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 @@ *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 @@ // // 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 --- 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 --- 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 --- 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 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -1085,41 +1085,201 @@ bool IsErrorExpected; }; -struct MaxOpsPerInstFixture - : TestWithParam>, - AdjustAddressFixtureBase { - void SetUp() override { - std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam(); - } - - uint64_t editPrologue(LineTable <) override { +struct OpIndexFixture : Test, CommonFixture { + void createPrologue(LineTable <, 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 < = 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 < = 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 < = 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 < = 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>, AdjustAddressFixtureBase { @@ -1572,22 +1732,26 @@ 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");