diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -98,42 +98,93 @@ return Merged; } +static auto TraverseDILocation(DILocation const *DL) { + class Range { + DILocation const *DL; + + public: + class Iterator { + DIScope *S; + DILocation *L; + unsigned Line; + unsigned Col; + + public: + Iterator(DILocation const *DL) + : S(DL->getScope()), L(DL->getInlinedAt()), Line(DL->getLine()), + Col(DL->getColumn()) {} + + std::pair, + std::pair> + operator*() const { + return std::make_pair(std::make_pair(S, L), std::make_pair(Line, Col)); + } + + struct EndSentinel {}; + bool operator!=(EndSentinel) const { + // This only checks if 'this' reached the end. Ignore the other + // iterator. + return S != nullptr; + } + + Iterator &operator++() { + S = S->getScope(); + if (!S && L) { + Line = Col = 0; + S = L->getScope(); + L = L->getInlinedAt(); + } + return *this; + } + }; + + Range(DILocation const *DL) : DL(DL) {} + Iterator begin() const { return DL; } + Iterator::EndSentinel end() const { return {}; } + }; + + return Range(DL); +} + const DILocation *DILocation::getMergedLocation(const DILocation *LocA, const DILocation *LocB) { if (!LocA || !LocB) return nullptr; - if (LocA == LocB) + if (LocA == LocB) { return LocA; + } + + LLVMContext &C = LocA->getContext(); + SmallDenseMap, + std::pair, 4> + Locations; + for (auto Loc : TraverseDILocation(LocA)) + Locations.insert(Loc); - SmallSet, 5> Locations; - DIScope *S = LocA->getScope(); - DILocation *L = LocA->getInlinedAt(); - while (S) { - Locations.insert(std::make_pair(S, L)); - S = S->getScope(); - if (!S && L) { - S = L->getScope(); - L = L->getInlinedAt(); + for (auto Loc : TraverseDILocation(LocB)) { + auto MatchLoc = Locations.find(Loc.first); + if (MatchLoc == Locations.end()) { + continue; } - } - S = LocB->getScope(); - L = LocB->getInlinedAt(); - while (S) { - if (Locations.count(std::make_pair(S, L))) - break; - S = S->getScope(); - if (!S && L) { - S = L->getScope(); - L = L->getInlinedAt(); + + if (!isa(Loc.first.first)) { + return DILocation::get(C, 0, 0, LocA->getScope(), Loc.first.second); + } + + unsigned Line = 0; + unsigned Col = 0; + // if the lines match, pick the leftmost column + if (MatchLoc->second.first == Loc.second.first) { + Line = Loc.second.first; + Col = std::min(Loc.second.second, MatchLoc->second.second); } + return DILocation::get(C, Line, Col, Loc.first.first, Loc.first.second); } - // If the two locations are irreconsilable, just pick one. This is misleading, - // but on the other hand, it's a "line 0" location. - if (!S || !isa(S)) - S = LocA->getScope(); - return DILocation::get(LocA->getContext(), 0, 0, S, L); + // If the two locations are irreconsilable, pick any scope. + // and return a "line 0" location + return DILocation::get(C, 0, 0, LocA->getScope()); } Optional DILocation::encodeDiscriminator(unsigned BD, unsigned DF, diff --git a/llvm/test/DebugInfo/return-same-line-merge.ll b/llvm/test/DebugInfo/return-same-line-merge.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/return-same-line-merge.ll @@ -0,0 +1,39 @@ +; RUN: opt -simplifycfg -S < %s | FileCheck %s +; +; Simplified from the following code: +; int foo() { +; if(c) { return a; } else { return b; } +; } +define i32 @foo(i32 %c, i32 %a, i32 %b) !dbg !4 { +; CHECK: define i32 @foo({{.*}}) !dbg [[FOO_SUBPROGRAM:![0-9]+]] +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C:%.*]], 0, !dbg [[DBG_CMP:![0-9]+]] +; CHECK-NEXT: [[A_B:%.*]] = select i1 [[TOBOOL]], i32 [[A:%.*]], i32 [[B:%.*]] +; CHECK-NEXT: ret i32 [[A_B]], !dbg [[DBG_RET:![0-9]+]] +; CHECK: [[DBG_CMP]] = !DILocation(line: 2, column: 1, scope: [[FOO_SUBPROGRAM]]) +; CHECK: [[DBG_RET]] = !DILocation(line: 4, column: 1, scope: [[FOO_SUBPROGRAM]]) +entry: + %tobool = icmp ne i32 %c, 0, !dbg !7 + br i1 %tobool, label %cond.true, label %cond.false, !dbg !8 + +cond.true: ; preds = %entry + ret i32 %a, !dbg !9 + +cond.false: ; preds = %entry + ret i32 %b, !dbg !10 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 16.0.0)", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !6) +!5 = !DISubroutineType(types: !6) +!6 = !{} +!7 = !DILocation(line: 2, column: 1, scope: !4) +!8 = !DILocation(line: 3, column: 1, scope: !4) +!9 = !DILocation(line: 4, column: 1, scope: !4) +!10 = !DILocation(line: 4, column: 2, scope: !4) diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -961,11 +961,24 @@ auto *A = DILocation::get(Context, 2, 7, N); auto *B = DILocation::get(Context, 2, 7, S); auto *M = DILocation::getMergedLocation(A, B); - EXPECT_EQ(0u, M->getLine()); // FIXME: Should this be 2? - EXPECT_EQ(0u, M->getColumn()); // FIXME: Should this be 7? + EXPECT_EQ(2u, M->getLine()); + EXPECT_EQ(7u, M->getColumn()); EXPECT_EQ(N, M->getScope()); } + { + // Same line, different column. + auto *A = DILocation::get(Context, 2, 7, N); + auto *B = DILocation::get(Context, 2, 10, S); + auto *M0 = DILocation::getMergedLocation(A, B); + auto *M1 = DILocation::getMergedLocation(B, A); + for (auto *M : {M0, M1}) { + EXPECT_EQ(2u, M->getLine()); + EXPECT_EQ(7u, M->getColumn()); + EXPECT_EQ(N, M->getScope()); + } + } + { // Different lines, same scopes. auto *A = DILocation::get(Context, 1, 6, N); @@ -1006,7 +1019,7 @@ EXPECT_EQ(I, M->getInlinedAt()); } - { + { // Completely different. auto *I = DILocation::get(Context, 2, 7, N); auto *A = DILocation::get(Context, 1, 6, S, I); @@ -1018,6 +1031,35 @@ EXPECT_EQ(S, M->getScope()); EXPECT_EQ(nullptr, M->getInlinedAt()); } + + // Two locations, same line/column different file, inlined at the same place + { + auto *FA = getFile(); + auto *FB = getFile(); + auto *FI = getFile(); + + auto *SPA = DISubprogram::getDistinct(Context, FA, "a", "a", FA, 0, nullptr, + 0, nullptr, 0, 0, DINode::FlagZero, + DISubprogram::SPFlagZero, nullptr); + + auto *SPB = DISubprogram::getDistinct(Context, FB, "b", "b", FB, 0, nullptr, + 0, nullptr, 0, 0, DINode::FlagZero, + DISubprogram::SPFlagZero, nullptr); + + auto *SPI = DISubprogram::getDistinct(Context, FI, "i", "i", FI, 0, nullptr, + 0, nullptr, 0, 0, DINode::FlagZero, + DISubprogram::SPFlagZero, nullptr); + + auto *I = DILocation::get(Context, 2, 7, SPI); + auto *A = DILocation::get(Context, 2, 7, SPA, I); + auto *B = DILocation::get(Context, 2, 7, SPB, I); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(0u, M->getLine()); + EXPECT_EQ(0u, M->getColumn()); + EXPECT_TRUE(isa(M->getScope())); + EXPECT_EQ(SPI, M->getScope()); + EXPECT_EQ(nullptr, M->getInlinedAt()); + } } TEST_F(DILocationTest, getDistinct) {