[llvm] [DebugInfo] Merge partially matching chains of textual inclusions (PR #125780)

Vladislav Dzhidzhoev via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 12 09:29:02 PST 2025


https://github.com/dzhidzhoev updated https://github.com/llvm/llvm-project/pull/125780

>From 44309240bca1c5817389210f9894994db3765c8b Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Tue, 4 Feb 2025 23:19:58 +0100
Subject: [PATCH 1/5] [DebugInfo] Merge partially matching chains of textual
 inclusions

Fixes https://github.com/llvm/llvm-project/issues/122846.

The cause of this issue was that GetNearestCommonScope within
getMergedLocation, which used to find a common scope for a matching
locations pair, ignored DILexicalBlockFile while walking up the parent chain
of incoming DILocations. Therefore, getMergedLocation could attach
DILocation with line number from one file to the scope from another
file. Whereas in the reproducer from the issue, both incoming locations
have the same line and column numbers, and they can be just attached to
one of the DILexicalBlockFile's (which represent the same file and
have the same scope but are not distinct).

To fix that issue and to be able to merge locations from different
included files,

1. inlinedAt chains processing code from
   12a7aea6b02288000ba6f5c477284b26df8dca01 was generalized so that
   the same algorithm can be used for processing inlinedAt chains and
   nested DILexicalBlocks with changing file attribute chains, as they
   both can be considered as textual inclusions (as proposed in
   https://github.com/llvm/llvm-project/issues/122846).

2. getMergedLocations tries to merge chains of inlined locations.
   Here, for each matching pair of locations from inlinedAt chains, chains
   of their parent scopes are traversed. Only scopes within the same
   DISubprogram are considered (as all pairs of locations from inlinedAt chains
   can only be merged if they belong to the same subprogram).

   Also, only those parent scopes are considered, that have
   a different file attribute value from the children scope, as they
   represent textual inclusions.

   Such parent scope chains from incoming locations LocA and LocB are
   processed in the way similar to
   12a7aea6b02288000ba6f5c477284b26df8dca01, and matching pairs
   from textual inclusion chains are being merged. The valid merge location
   for the most local to LocA and LocB matching pair from parent scope chains
   is taken as getMergedLocation result.
---
 llvm/lib/IR/DebugInfoMetadata.cpp             | 297 ++++++++++++++----
 .../AArch64/merge-nested-block-loc.ll         | 204 ++++++++++++
 llvm/unittests/IR/MetadataTest.cpp            | 121 ++++++-
 3 files changed, 547 insertions(+), 75 deletions(-)
 create mode 100644 llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll

diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 915cdd301f2c7..1514455c60916 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -118,30 +118,40 @@ DILocation *DILocation::getMergedLocations(ArrayRef<DILocation *> Locs) {
   return Merged;
 }
 
-DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
-  if (!LocA || !LocB)
-    return nullptr;
-
-  if (LocA == LocB)
-    return LocA;
-
-  LLVMContext &C = LocA->getContext();
-
-  using LocVec = SmallVector<const DILocation *>;
+/// Merge two locations that may be nested as textual inclusions,
+/// e.g. via inlinedAt or nested LexicalBlocks.
+///
+/// \param GetLocationKey   Function to determine whether locations/scopes are considered matching.
+/// \param ShouldStop       Function that determines if include chain of a location has ended.
+/// \param NextLoc          Should return next location/object in include chain.
+/// \param MergeLocPair     Function to merge two possibly matching locations from
+///                         include chain.
+/// \param DefaultLocation  Function that returns location of the first matching nested object.
+/// \param LocA             First incoming location.
+/// \param LocA             Second incoming location.
+template <typename LocationKey, typename GetLocationKeyFn,
+          typename ShouldStopFn, typename NextLocFn, typename MergeLocPairFn,
+          typename DefaultLocationFn>
+static DILocation *
+MergeNestedLocations(GetLocationKeyFn GetLocationKey, ShouldStopFn ShouldStop,
+                     NextLocFn NextLoc, MergeLocPairFn MergeLocPair,
+                     DefaultLocationFn DefaultLocation, DILocation *LocA,
+                     DILocation *LocB) {
+  using LocVec = SmallVector<MDNode *>;
   LocVec ALocs;
   LocVec BLocs;
-  SmallDenseMap<std::pair<const DISubprogram *, const DILocation *>, unsigned,
-                4>
-      ALookup;
 
-  // Walk through LocA and its inlined-at locations, populate them in ALocs and
-  // save the index for the subprogram and inlined-at pair, which we use to find
+  LLVMContext &C = LocA->getContext();
+
+  SmallDenseMap<LocationKey, unsigned, 4> ALookup;
+  // Walk through LocA, populate them in ALocs and
+  // save the index, which we use to find
   // a matching starting location in LocB's chain.
-  for (auto [L, I] = std::make_pair(LocA, 0U); L; L = L->getInlinedAt(), I++) {
+  using iter_t = std::pair<MDNode *, unsigned>;
+  for (auto [L, I] = iter_t(LocA, 0U); ShouldStop(L); L = NextLoc(L), I++) {
     ALocs.push_back(L);
-    auto Res = ALookup.try_emplace(
-        {L->getScope()->getSubprogram(), L->getInlinedAt()}, I);
-    assert(Res.second && "Multiple <SP, InlinedAt> pairs in a location chain?");
+    auto Res = ALookup.try_emplace(GetLocationKey(L), I);
+    assert(Res.second && "Multiple pairs in a location chain?");
     (void)Res;
   }
 
@@ -149,17 +159,17 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
   LocVec::reverse_iterator BRIt = BLocs.rend();
 
   // Populate BLocs and look for a matching starting location, the first
-  // location with the same subprogram and inlined-at location as in LocA's
-  // chain. Since the two locations have the same inlined-at location we do
+  // location with the same key as in LocA's
+  // chain. Since the two locations have the same key we do
   // not need to look at those parts of the chains.
-  for (auto [L, I] = std::make_pair(LocB, 0U); L; L = L->getInlinedAt(), I++) {
+  for (auto [L, I] = iter_t(LocB, 0U); ShouldStop(L); L = NextLoc(L), I++) {
     BLocs.push_back(L);
 
     if (ARIt != ALocs.rend())
       // We have already found a matching starting location.
       continue;
 
-    auto IT = ALookup.find({L->getScope()->getSubprogram(), L->getInlinedAt()});
+    auto IT = ALookup.find(GetLocationKey(L));
     if (IT == ALookup.end())
       continue;
 
@@ -174,10 +184,131 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
     break;
   }
 
+  DILocation *Result = ARIt != ALocs.rend() ? DefaultLocation(*ARIt) : nullptr;
+
+  // If we have found a common starting location, walk up the chains
+  // and try to produce common locations.
+  for (; ARIt != ALocs.rend() && BRIt != BLocs.rend(); ++ARIt, ++BRIt) {
+    DILocation *Tmp = MergeLocPair(*ARIt, *BRIt, Result);
+
+    if (!Tmp)
+      // We have walked up to a point in te chains where the two locations
+      // are irreconsilable. At this point Result contains the nearest common
+      // location in the location chains of LocA and LocB, so we break here.
+      break;
+
+    Result = Tmp;
+  }
+
+  if (auto *ResultL = dyn_cast_if_present<DILocation>(Result))
+    return ResultL;
+
+  // We ended up with LocA and LocB as irreconsilable locations. Produce a
+  // location at 0:0 with one of the locations' scope.
+  return DILocation::get(C, 0, 0, LocA->getScope(), nullptr);
+}
+
+/// Returns the nearest common scope for two locations inside a subprogram.
+static DIScope *GetNearestCommonScope(DILocation *L1, DILocation *L2) {
+  DIScope *S1 = L1->getScope();
+  DIScope *S2 = L2->getScope();
+
+  // Try matching DILexicalBlockFiles enclosing L1, L2.
+  if (auto *LBF1 = dyn_cast<DILexicalBlockFile>(S1)) {
+    if (auto *LBF2 = dyn_cast<DILexicalBlockFile>(S2)) {
+      if (LBF1->getFile() && LBF1->getFile() == LBF2->getFile() &&
+          LBF1->getDiscriminator() == LBF2->getDiscriminator()) {
+        return LBF1;
+      }
+    }
+  }
+
+  SmallPtrSet<DIScope *, 8> Scopes;
+  for (; S1; S1 = S1->getScope()) {
+    Scopes.insert(S1);
+    if (isa<DISubprogram>(S1))
+      break;
+  }
+
+  for (; S2; S2 = S2->getScope()) {
+    if (Scopes.count(S2))
+      return S2;
+    if (isa<DISubprogram>(S2))
+      break;
+  }
+
+  return nullptr;
+}
+
+/// Walk up the chain of parent local scopes until the file has changed
+/// or DISubprogram reached.
+static DILocalScope *NextScopeWithDifferentFile(MDNode *LocOrScope) {
+  assert((isa<DILocation>(LocOrScope) || isa<DILocalScope>(LocOrScope)) &&
+         "DILocation or DILocalScope expected");
+
+  DIScope *S = nullptr;
+  DIFile *F = nullptr;
+  if (auto *Loc = dyn_cast<DILocation>(LocOrScope)) {
+    S = Loc->getScope();
+    F = Loc->getFile();
+  } else {
+    auto *LS = dyn_cast<DILocalScope>(LocOrScope);
+    S = LS->getScope();
+    F = LS->getFile();
+  }
+
+  while (isa_and_present<DILocalScope>(S) && !isa<DISubprogram>(S)) {
+    if (auto *LB = dyn_cast_if_present<DILexicalBlock>(S)) {
+      if (F != LB->getFile()) {
+        return LB;
+      }
+    }
+    S = S->getScope();
+  }
+
+  if (auto *SP = dyn_cast_or_null<DISubprogram>(S)) {
+    return SP;
+  }
+
+  return nullptr;
+}
+
+/// Get DILocation and it's DIFile from either DILocation, DISubprogram or
+/// DILexicalBlock.
+static std::pair<DILocation *, DIFile *>
+GetLocAndFile(LLVMContext &C, MDNode *N, DILocation *InlinedAt) {
+  DILocation *Loc = nullptr;
+  DIFile *File = nullptr;
+  if (auto *InputLoc = dyn_cast<DILocation>(N)) {
+    Loc = InputLoc;
+    File = InputLoc->getFile();
+  } else if (auto *LB = dyn_cast<DILexicalBlock>(N)) {
+    Loc = DILocation::get(C, LB->getLine(), LB->getColumn(), LB, InlinedAt);
+    File = LB->getFile();
+  } else if (auto *SP = dyn_cast<DISubprogram>(N)) {
+    Loc = DILocation::get(C, SP->getLine(), 0, SP, InlinedAt);
+    File = SP->getFile();
+  }
+
+  return std::make_pair(Loc, File);
+}
+
+DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
+  if (!LocA || !LocB)
+    return nullptr;
+
+  if (LocA == LocB)
+    return LocA;
+
+  LLVMContext &C = LocA->getContext();
+
   // Merge the two locations if possible, using the supplied
   // inlined-at location for the created location.
-  auto MergeLocPair = [&C](const DILocation *L1, const DILocation *L2,
-                           DILocation *InlinedAt) -> DILocation * {
+  auto MergeInlinedLocations = [&C](MDNode *N1, MDNode *N2,
+                                    DILocation *InlinedAt) -> DILocation * {
+    auto *L1 = cast<DILocation>(N1);
+    auto *L2 = cast<DILocation>(N2);
+
     if (L1 == L2)
       return DILocation::get(C, L1->getLine(), L1->getColumn(), L1->getScope(),
                              InlinedAt);
@@ -187,61 +318,89 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
     if (L1->getScope()->getSubprogram() != L2->getScope()->getSubprogram())
       return nullptr;
 
-    // Return the nearest common scope inside a subprogram.
-    auto GetNearestCommonScope = [](DIScope *S1, DIScope *S2) -> DIScope * {
-      SmallPtrSet<DIScope *, 8> Scopes;
-      for (; S1; S1 = S1->getScope()) {
-        Scopes.insert(S1);
-        if (isa<DISubprogram>(S1))
-          break;
-      }
-
-      for (; S2; S2 = S2->getScope()) {
-        if (Scopes.count(S2))
-          return S2;
-        if (isa<DISubprogram>(S2))
-          break;
+    // We should traverse DILexicalBlock's that enclose input locations and
+    // have different file attribute.
+    using NestedLexicalBlocksMatchKey = MDNode *;
+    // We try to match equal scopes or locations belonging to the same file.
+    auto GetLocOrScopeMatchKey = [](MDNode *N) -> NestedLexicalBlocksMatchKey {
+      if (auto *Loc = dyn_cast_if_present<DILocation>(N)) {
+        return Loc->getFile();
+      } else if (auto *LS = dyn_cast_if_present<DILocalScope>(N)) {
+        return LS;
       }
 
       return nullptr;
     };
 
-    auto Scope = GetNearestCommonScope(L1->getScope(), L2->getScope());
-    assert(Scope && "No common scope in the same subprogram?");
-
-    bool SameLine = L1->getLine() == L2->getLine();
-    bool SameCol = L1->getColumn() == L2->getColumn();
-    unsigned Line = SameLine ? L1->getLine() : 0;
-    unsigned Col = SameLine && SameCol ? L1->getColumn() : 0;
-
-    return DILocation::get(C, Line, Col, Scope, InlinedAt);
-  };
+    auto MergeTextualInclusions =
+        [&C, InlinedAt](MDNode *LocOrScope1, MDNode *LocOrScope2,
+                        DILocation *PrevLoc) -> DILocation * {
+      assert((isa_and_present<DILocation>(LocOrScope1) ||
+              isa_and_present<DILocalScope>(LocOrScope1)) &&
+             "Incorrect nested location type");
+      assert((isa_and_present<DILocation>(LocOrScope2) ||
+              isa_and_present<DILocalScope>(LocOrScope2)) &&
+             "Incorrect nested location type");
+
+      auto [L1, F1] = GetLocAndFile(C, LocOrScope1, InlinedAt);
+      auto [L2, F2] = GetLocAndFile(C, LocOrScope2, InlinedAt);
+
+      assert(L1->getScope()->getSubprogram() ==
+                 L2->getScope()->getSubprogram() &&
+             "Locations from different subprograms?");
+
+      if (L1 == L2) {
+        auto *Result = DILocation::get(C, L1->getLine(), L1->getColumn(),
+                                       L1->getScope(), InlinedAt);
+        return Result;
+      }
 
-  DILocation *Result = ARIt != ALocs.rend() ? (*ARIt)->getInlinedAt() : nullptr;
+      DIScope *Scope = GetNearestCommonScope(L1, L2);
+      assert(Scope && "No common scope in the same subprogram?");
 
-  // If we have found a common starting location, walk up the inlined-at chains
-  // and try to produce common locations.
-  for (; ARIt != ALocs.rend() && BRIt != BLocs.rend(); ++ARIt, ++BRIt) {
-    DILocation *Tmp = MergeLocPair(*ARIt, *BRIt, Result);
+      // If the common scope from the same file as incoming locations
+      // can't be found, try another pair of locations.
+      if (Scope->getFile() != F1 || Scope->getFile() != F2) {
+        return nullptr;
+      }
 
-    if (!Tmp)
-      // We have walked up to a point in the chains where the two locations
-      // are irreconsilable. At this point Result contains the nearest common
-      // location in the inlined-at chains of LocA and LocB, so we break here.
-      break;
+      bool SameLine = L1->getLine() == L2->getLine();
+      bool SameCol = L1->getColumn() == L2->getColumn();
+      unsigned Line = SameLine ? L1->getLine() : 0;
+      unsigned Col = SameLine && SameCol ? L1->getColumn() : 0;
 
-    Result = Tmp;
-  }
+      auto *Result = DILocation::get(C, Line, Col, Scope, InlinedAt);
+      return Result;
+    };
 
-  if (Result)
-    return Result;
+    return MergeNestedLocations<NestedLexicalBlocksMatchKey>(
+        GetLocOrScopeMatchKey,
+        [](MDNode *L) -> bool { return L; },
+        NextScopeWithDifferentFile,
+        MergeTextualInclusions,
+        [](MDNode *N) { return nullptr; },
+        L1, L2);
+  };
 
-  // We ended up with LocA and LocB as irreconsilable locations. Produce a
-  // location at 0:0 with one of the locations' scope. The function has
-  // historically picked A's scope, and a nullptr inlined-at location, so that
-  // behavior is mimicked here but I am not sure if this is always the correct
-  // way to handle this.
-  return DILocation::get(C, 0, 0, LocA->getScope(), nullptr);
+  // When traversing inlinedAt chain, we consider locations within the same
+  // subprogram and inlinedAt location matching.
+  using InlinedAtLookupKey = std::pair<DISubprogram *, DILocation *>;
+  auto GetInlinedAtKey = [](MDNode *N) {
+    auto *L = cast<DILocation>(N);
+    return InlinedAtLookupKey(L->getScope()->getSubprogram(),
+                              L->getInlinedAt());
+  };
+  auto NextInlinedAt = [](MDNode *N) {
+    auto *L = cast<DILocation>(N);
+    return L->getInlinedAt();
+  };
+  return MergeNestedLocations<InlinedAtLookupKey>(
+      GetInlinedAtKey,
+      [](MDNode *N) { return N; },
+      NextInlinedAt,
+      MergeInlinedLocations,
+      [GetInlinedAtKey](MDNode *N) { return GetInlinedAtKey(N).second; },
+      LocA, LocB);
 }
 
 std::optional<unsigned>
diff --git a/llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll b/llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll
new file mode 100644
index 0000000000000..ed087c1e90166
--- /dev/null
+++ b/llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll
@@ -0,0 +1,204 @@
+; RUN: opt -mtriple=aarch64-unknown-linux-gnu -S %s -passes=sroa -o - | FileCheck %s
+
+; In this test we want to ensure that the location of phi instruction merged from locations
+; of %mul3 and %mul9 belongs to the correct scope (DILexicalBlockFile), so that line
+; number of that location belongs to the corresponding file.
+
+; Generated with clang from
+;  1 # 1 "1.c" 1
+;  2 # 1 "1.c" 2
+;  3 int foo(int a) {
+;  4   int i = 0;
+;  5   if ((a & 1) == 1) {
+;  6     a -= 1;
+;  7 # 1 "m.c" 1
+;  8 # 40 "m.c"
+;  9 i += a;
+; 10 i -= 10*a;
+; 11 i *= a*a;
+; 12 # 6 "1.c" 2
+; 13  } else {
+; 14     a += 3;
+; 15 # 1 "m.c" 1
+; 16 # 40 "m.c"
+; 17 i += a;
+; 18 i -= 10*a;
+; 19 i *= a*a;
+; 20 # 9 "1.c" 2
+; 21  }
+; 22   return i;
+; 23 }
+
+; ModuleID = 'repro.c'
+source_filename = "repro.c"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64-unknown-linux-gnu"
+
+; Function Attrs: nounwind uwtable
+define dso_local i32 @foo(i32 noundef %a) #0 !dbg !9 {
+; CHECK:    phi i32 {{.*}}, !dbg [[PHILOC:![0-9]+]]
+;
+entry:
+  %a.addr = alloca i32, align 4, !DIAssignID !17
+  #dbg_assign(i1 undef, !15, !DIExpression(), !17, ptr %a.addr, !DIExpression(), !18)
+  %i = alloca i32, align 4, !DIAssignID !19
+  #dbg_assign(i1 undef, !16, !DIExpression(), !19, ptr %i, !DIExpression(), !18)
+  store i32 %a, ptr %a.addr, align 4, !tbaa !20, !DIAssignID !24
+  #dbg_assign(i32 %a, !15, !DIExpression(), !24, ptr %a.addr, !DIExpression(), !18)
+  call void @llvm.lifetime.start.p0(i64 4, ptr %i) #2, !dbg !25
+  store i32 0, ptr %i, align 4, !dbg !26, !tbaa !20, !DIAssignID !27
+  #dbg_assign(i32 0, !16, !DIExpression(), !27, ptr %i, !DIExpression(), !18)
+  %0 = load i32, ptr %a.addr, align 4, !dbg !28, !tbaa !20
+  %and = and i32 %0, 1, !dbg !30
+  %cmp = icmp eq i32 %and, 1, !dbg !31
+  br i1 %cmp, label %if.then, label %if.else, !dbg !31
+
+if.then:                                          ; preds = %entry
+  %1 = load i32, ptr %a.addr, align 4, !dbg !32, !tbaa !20
+  %sub = sub nsw i32 %1, 1, !dbg !32
+  store i32 %sub, ptr %a.addr, align 4, !dbg !32, !tbaa !20, !DIAssignID !34
+  #dbg_assign(i32 %sub, !15, !DIExpression(), !34, ptr %a.addr, !DIExpression(), !18)
+  %2 = load i32, ptr %a.addr, align 4, !dbg !35, !tbaa !20
+  %3 = load i32, ptr %i, align 4, !dbg !38, !tbaa !20
+  %add = add nsw i32 %3, %2, !dbg !38
+  store i32 %add, ptr %i, align 4, !dbg !38, !tbaa !20, !DIAssignID !39
+  #dbg_assign(i32 %add, !16, !DIExpression(), !39, ptr %i, !DIExpression(), !18)
+  %4 = load i32, ptr %a.addr, align 4, !dbg !40, !tbaa !20
+  %mul = mul nsw i32 10, %4, !dbg !41
+  %5 = load i32, ptr %i, align 4, !dbg !42, !tbaa !20
+  %sub1 = sub nsw i32 %5, %mul, !dbg !42
+  store i32 %sub1, ptr %i, align 4, !dbg !42, !tbaa !20, !DIAssignID !43
+  #dbg_assign(i32 %sub1, !16, !DIExpression(), !43, ptr %i, !DIExpression(), !18)
+  %6 = load i32, ptr %a.addr, align 4, !dbg !44, !tbaa !20
+  %7 = load i32, ptr %a.addr, align 4, !dbg !45, !tbaa !20
+  %mul2 = mul nsw i32 %6, %7, !dbg !46
+  %8 = load i32, ptr %i, align 4, !dbg !47, !tbaa !20
+  %mul3 = mul nsw i32 %8, %mul2, !dbg !47
+  store i32 %mul3, ptr %i, align 4, !dbg !47, !tbaa !20, !DIAssignID !48
+  #dbg_assign(i32 %mul3, !16, !DIExpression(), !48, ptr %i, !DIExpression(), !18)
+  br label %if.end, !dbg !49
+
+if.else:                                          ; preds = %entry
+  %9 = load i32, ptr %a.addr, align 4, !dbg !51, !tbaa !20
+  %add4 = add nsw i32 %9, 3, !dbg !51
+  store i32 %add4, ptr %a.addr, align 4, !dbg !51, !tbaa !20, !DIAssignID !53
+  #dbg_assign(i32 %add4, !15, !DIExpression(), !53, ptr %a.addr, !DIExpression(), !18)
+  %10 = load i32, ptr %a.addr, align 4, !dbg !54, !tbaa !20
+  %11 = load i32, ptr %i, align 4, !dbg !56, !tbaa !20
+  %add5 = add nsw i32 %11, %10, !dbg !56
+  store i32 %add5, ptr %i, align 4, !dbg !56, !tbaa !20, !DIAssignID !57
+  #dbg_assign(i32 %add5, !16, !DIExpression(), !57, ptr %i, !DIExpression(), !18)
+  %12 = load i32, ptr %a.addr, align 4, !dbg !58, !tbaa !20
+  %mul6 = mul nsw i32 10, %12, !dbg !59
+  %13 = load i32, ptr %i, align 4, !dbg !60, !tbaa !20
+  %sub7 = sub nsw i32 %13, %mul6, !dbg !60
+  store i32 %sub7, ptr %i, align 4, !dbg !60, !tbaa !20, !DIAssignID !61
+  #dbg_assign(i32 %sub7, !16, !DIExpression(), !61, ptr %i, !DIExpression(), !18)
+  %14 = load i32, ptr %a.addr, align 4, !dbg !62, !tbaa !20
+  %15 = load i32, ptr %a.addr, align 4, !dbg !63, !tbaa !20
+  %mul8 = mul nsw i32 %14, %15, !dbg !64
+  %16 = load i32, ptr %i, align 4, !dbg !65, !tbaa !20
+  %mul9 = mul nsw i32 %16, %mul8, !dbg !65
+  store i32 %mul9, ptr %i, align 4, !dbg !65, !tbaa !20, !DIAssignID !66
+  #dbg_assign(i32 %mul9, !16, !DIExpression(), !66, ptr %i, !DIExpression(), !18)
+  br label %if.end
+
+if.end:                                           ; preds = %if.else, %if.then
+  %17 = load i32, ptr %i, align 4, !dbg !67, !tbaa !20
+  call void @llvm.lifetime.end.p0(i64 4, ptr %i) #2, !dbg !68
+  ret i32 %17, !dbg !69
+}
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1
+
+attributes #0 = { nounwind uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+v8a,-fmv" }
+attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+attributes #2 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
+!llvm.ident = !{!8}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 20.0.0git (git at github.com:llvm/llvm-project.git c8ee1164bd6ae2f0a603c53d1d29ad5a3225c5cd)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "repro.c", directory: "", checksumkind: CSK_MD5, checksum: "51454d2babc57d5ea92df6734236bd39")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"uwtable", i32 2}
+!6 = !{i32 7, !"frame-pointer", i32 1}
+!7 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!8 = !{!"clang version 20.0.0git (git at github.com:llvm/llvm-project.git c8ee1164bd6ae2f0a603c53d1d29ad5a3225c5cd)"}
+!9 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
+!10 = !DIFile(filename: "1.c", directory: "")
+!11 = !DISubroutineType(types: !12)
+!12 = !{!13, !13}
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{!15, !16}
+!15 = !DILocalVariable(name: "a", arg: 1, scope: !9, file: !10, line: 1, type: !13)
+!16 = !DILocalVariable(name: "i", scope: !9, file: !10, line: 2, type: !13)
+!17 = distinct !DIAssignID()
+!18 = !DILocation(line: 0, scope: !9)
+!19 = distinct !DIAssignID()
+!20 = !{!21, !21, i64 0}
+!21 = !{!"int", !22, i64 0}
+!22 = !{!"omnipotent char", !23, i64 0}
+!23 = !{!"Simple C/C++ TBAA"}
+!24 = distinct !DIAssignID()
+!25 = !DILocation(line: 2, column: 3, scope: !9)
+!26 = !DILocation(line: 2, column: 7, scope: !9)
+!27 = distinct !DIAssignID()
+!28 = !DILocation(line: 3, column: 8, scope: !29)
+!29 = distinct !DILexicalBlock(scope: !9, file: !10, line: 3, column: 7)
+!30 = !DILocation(line: 3, column: 10, scope: !29)
+!31 = !DILocation(line: 3, column: 15, scope: !29)
+!32 = !DILocation(line: 4, column: 7, scope: !33)
+!33 = distinct !DILexicalBlock(scope: !29, file: !10, line: 3, column: 21)
+!34 = distinct !DIAssignID()
+!35 = !DILocation(line: 40, column: 6, scope: !36)
+!36 = !DILexicalBlockFile(scope: !33, file: !37, discriminator: 0)
+!37 = !DIFile(filename: "m.c", directory: "")
+!38 = !DILocation(line: 40, column: 3, scope: !36)
+!39 = distinct !DIAssignID()
+!40 = !DILocation(line: 41, column: 9, scope: !36)
+!41 = !DILocation(line: 41, column: 8, scope: !36)
+!42 = !DILocation(line: 41, column: 3, scope: !36)
+!43 = distinct !DIAssignID()
+!44 = !DILocation(line: 42, column: 6, scope: !36)
+!45 = !DILocation(line: 42, column: 8, scope: !36)
+!46 = !DILocation(line: 42, column: 7, scope: !36)
+!47 = !DILocation(line: 42, column: 3, scope: !36)
+!48 = distinct !DIAssignID()
+!49 = !DILocation(line: 6, column: 2, scope: !50)
+!50 = !DILexicalBlockFile(scope: !33, file: !10, discriminator: 0)
+!51 = !DILocation(line: 7, column: 7, scope: !52)
+!52 = distinct !DILexicalBlock(scope: !29, file: !10, line: 6, column: 9)
+!53 = distinct !DIAssignID()
+!54 = !DILocation(line: 40, column: 6, scope: !55)
+!55 = !DILexicalBlockFile(scope: !52, file: !37, discriminator: 0)
+!56 = !DILocation(line: 40, column: 3, scope: !55)
+!57 = distinct !DIAssignID()
+!58 = !DILocation(line: 41, column: 9, scope: !55)
+!59 = !DILocation(line: 41, column: 8, scope: !55)
+!60 = !DILocation(line: 41, column: 3, scope: !55)
+!61 = distinct !DIAssignID()
+!62 = !DILocation(line: 42, column: 6, scope: !55)
+!63 = !DILocation(line: 42, column: 8, scope: !55)
+!64 = !DILocation(line: 42, column: 7, scope: !55)
+!65 = !DILocation(line: 42, column: 3, scope: !55)
+!66 = distinct !DIAssignID()
+!67 = !DILocation(line: 10, column: 10, scope: !9)
+!68 = !DILocation(line: 11, column: 1, scope: !9)
+!69 = !DILocation(line: 10, column: 3, scope: !9)
+
+;.
+; CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "foo", scope: [[FILE1:![0-9]+]], file: [[FILE1]], line: 1
+; CHECK: [[FILE1]] = !DIFile(filename: "1.c", directory: "")
+; CHECK: [[LB1:![0-9]+]] = distinct !DILexicalBlock(scope: [[SP]], file: [[FILE1]], line: 3, column: 7)
+; CHECK: [[LB2:![0-9]+]] = distinct !DILexicalBlock(scope: [[LB1]], file: [[FILE1]], line: 3, column: 21)
+; CHECK: [[LBF:![0-9]+]] = !DILexicalBlockFile(scope: [[LB2]], file: [[FILE2:![0-9]+]], discriminator: 0)
+; CHECK: [[FILE2]] = !DIFile(filename: "m.c", directory: "")
+; CHECK: [[PHILOC]] = !DILocation(line: 42, column: 3, scope: [[LBF]])
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 628221339c89b..cc13ef6ad9da0 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -85,10 +85,10 @@ class MetadataTest : public testing::Test {
     return DISubroutineType::getDistinct(Context, DINode::FlagZero, 0,
                                          getNode(nullptr));
   }
-  DISubprogram *getSubprogram() {
-    return DISubprogram::getDistinct(
-        Context, nullptr, "", "", nullptr, 0, nullptr, 0, nullptr, 0, 0,
-        DINode::FlagZero, DISubprogram::SPFlagZero, nullptr);
+  DISubprogram *getSubprogram(DIFile *F = nullptr) {
+    return DISubprogram::getDistinct(Context, nullptr, "", "", F, 0, nullptr, 0,
+                                     nullptr, 0, 0, DINode::FlagZero,
+                                     DISubprogram::SPFlagZero, nullptr);
   }
   DIFile *getFile() {
     return DIFile::getDistinct(Context, "file.c", "/path/to/dir");
@@ -918,8 +918,9 @@ TEST_F(MDNodeTest, deleteTemporaryWithTrackingRef) {
 typedef MetadataTest DILocationTest;
 
 TEST_F(DILocationTest, Merge) {
-  DISubprogram *N = getSubprogram();
-  DIScope *S = DILexicalBlock::get(Context, N, getFile(), 3, 4);
+  DIFile *F = getFile();
+  DISubprogram *N = getSubprogram(F);
+  DIScope *S = DILexicalBlock::get(Context, N, F, 3, 4);
 
   {
     // Identical.
@@ -974,6 +975,114 @@ TEST_F(DILocationTest, Merge) {
     EXPECT_EQ(N, M->getScope());
   }
 
+  {
+    // Different files, same line numbers, same subprogram.
+    auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir");
+    auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir");
+    DISubprogram *N = getSubprogram(F1);
+    auto *LBF = DILexicalBlockFile::get(Context, N, F2, 0);
+    auto *A = DILocation::get(Context, 1, 6, N);
+    auto *B = DILocation::get(Context, 1, 6, LBF);
+    auto *M = DILocation::getMergedLocation(A, B);
+    EXPECT_EQ(0u, M->getLine());
+    EXPECT_EQ(0u, M->getColumn());
+    EXPECT_EQ(N, M->getScope());
+  }
+
+  {
+    // Different files, same line numbers.
+    auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir");
+    auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir");
+    DISubprogram *N = getSubprogram(F1);
+    auto *LB = DILexicalBlock::getDistinct(Context, N, F1, 4, 9);
+    auto *LBF = DILexicalBlockFile::get(Context, LB, F2, 0);
+    auto *A = DILocation::get(Context, 1, 6, LB);
+    auto *B = DILocation::get(Context, 1, 6, LBF);
+    auto *M = DILocation::getMergedLocation(A, B);
+    EXPECT_EQ(0u, M->getLine());
+    EXPECT_EQ(0u, M->getColumn());
+    EXPECT_EQ(LB, M->getScope());
+  }
+
+  {
+    // Different files, same line numbers,
+    // both locations have DILexicalBlockFile scopes.
+    auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir");
+    auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir");
+    auto *F3 = DIFile::getDistinct(Context, "file3.c", "/path/to/dir");
+    DISubprogram *N = getSubprogram(F1);
+    auto *LB = DILexicalBlock::getDistinct(Context, N, F1, 4, 9);
+    auto *LBF1 = DILexicalBlockFile::get(Context, LB, F2, 0);
+    auto *LBF2 = DILexicalBlockFile::get(Context, LB, F3, 0);
+    auto *A = DILocation::get(Context, 1, 6, LBF1);
+    auto *B = DILocation::get(Context, 1, 6, LBF2);
+    auto *M = DILocation::getMergedLocation(A, B);
+    EXPECT_EQ(4u, M->getLine());
+    EXPECT_EQ(9u, M->getColumn());
+    EXPECT_EQ(LB, M->getScope());
+  }
+
+  {
+    // Same file, same line numbers, but different LBF objects.
+    // both locations have DILexicalBlockFile scope.
+    auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir");
+    DISubprogram *N = getSubprogram(F1);
+    auto *LB1 = DILexicalBlock::getDistinct(Context, N, F1, 4, 9);
+    auto *LB2 = DILexicalBlock::getDistinct(Context, N, F1, 5, 9);
+    auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir");
+    auto *LBF1 = DILexicalBlockFile::get(Context, LB1, F2, 0);
+    auto *LBF2 = DILexicalBlockFile::get(Context, LB2, F2, 0);
+    auto *A = DILocation::get(Context, 1, 6, LBF1);
+    auto *B = DILocation::get(Context, 1, 6, LBF2);
+    auto *M = DILocation::getMergedLocation(A, B);
+    EXPECT_EQ(1u, M->getLine());
+    EXPECT_EQ(6u, M->getColumn());
+    EXPECT_EQ(LBF1, M->getScope());
+  }
+
+  {
+    // Merge locations A and B, where B is included in A's file
+    // at the same position as A's position.
+    auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir");
+    DISubprogram *N = getSubprogram(F1);
+    auto *LB = DILexicalBlock::getDistinct(Context, N, F1, 4, 9);
+    auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir");
+    auto *LBF = DILexicalBlockFile::get(Context, LB, F2, 0);
+    auto *A = DILocation::get(Context, 4, 9, LB);
+    auto *B = DILocation::get(Context, 1, 6, LBF);
+    auto *M = DILocation::getMergedLocation(A, B);
+    EXPECT_EQ(4u, M->getLine());
+    EXPECT_EQ(9u, M->getColumn());
+    EXPECT_EQ(LB, M->getScope());
+  }
+
+  {
+    // Different locations from different files having common lexical blocks
+    auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir");
+    DISubprogram *N = getSubprogram(F1);
+
+    auto *LBCommon = DILexicalBlock::getDistinct(Context, N, F1, 4, 9);
+
+    auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir");
+    LBCommon = DILexicalBlock::getDistinct(Context, LBCommon, F2, 5, 9);
+
+    auto *F3 = DIFile::getDistinct(Context, "file3.c", "/path/to/dir");
+    auto *LB1 = DILexicalBlock::getDistinct(Context, LBCommon, F3, 6, 9);
+
+    auto *F4 = DIFile::getDistinct(Context, "file4.c", "/path/to/dir");
+    auto *LB2 = DILexicalBlock::getDistinct(Context, LBCommon, F4, 7, 9);
+
+    auto *F5 = DIFile::getDistinct(Context, "file5.c", "/path/to/dir");
+    auto *LBF1 = DILexicalBlockFile::get(Context, LB1, F5, 0);
+
+    auto *A = DILocation::get(Context, 8, 9, LB2);
+    auto *B = DILocation::get(Context, 9, 6, LBF1);
+    auto *M = DILocation::getMergedLocation(A, B);
+    EXPECT_EQ(5u, M->getLine());
+    EXPECT_EQ(9u, M->getColumn());
+    EXPECT_EQ(LBCommon, M->getScope());
+  }
+
   {
     // Different function, same inlined-at.
     auto *F = getFile();

>From 6548856fe393fbbf5134433aba4fed1de49e9f8f Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Wed, 5 Feb 2025 00:26:06 +0100
Subject: [PATCH 2/5] Clang-format

---
 llvm/lib/IR/DebugInfoMetadata.cpp | 36 +++++++++++++++----------------
 1 file changed, 17 insertions(+), 19 deletions(-)

diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 1514455c60916..359721c215c05 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -121,12 +121,15 @@ DILocation *DILocation::getMergedLocations(ArrayRef<DILocation *> Locs) {
 /// Merge two locations that may be nested as textual inclusions,
 /// e.g. via inlinedAt or nested LexicalBlocks.
 ///
-/// \param GetLocationKey   Function to determine whether locations/scopes are considered matching.
-/// \param ShouldStop       Function that determines if include chain of a location has ended.
+/// \param GetLocationKey   Function to determine whether locations/scopes are
+/// considered matching.
+/// \param ShouldStop       Function that determines if include chain of
+/// location has ended.
 /// \param NextLoc          Should return next location/object in include chain.
-/// \param MergeLocPair     Function to merge two possibly matching locations from
-///                         include chain.
-/// \param DefaultLocation  Function that returns location of the first matching nested object.
+/// \param MergeLocPair     Function to merge two possibly matching locations
+/// from include chain.
+/// \param DefaultLocation  Function that returns location of the first matching
+/// nested object.
 /// \param LocA             First incoming location.
 /// \param LocA             Second incoming location.
 template <typename LocationKey, typename GetLocationKeyFn,
@@ -302,10 +305,12 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
 
   LLVMContext &C = LocA->getContext();
 
+  auto IsChainOver = [](MDNode *L) -> bool { return L; };
   // Merge the two locations if possible, using the supplied
   // inlined-at location for the created location.
-  auto MergeInlinedLocations = [&C](MDNode *N1, MDNode *N2,
-                                    DILocation *InlinedAt) -> DILocation * {
+  auto MergeInlinedLocations =
+      [&C, IsChainOver](MDNode *N1, MDNode *N2,
+                        DILocation *InlinedAt) -> DILocation * {
     auto *L1 = cast<DILocation>(N1);
     auto *L2 = cast<DILocation>(N2);
 
@@ -374,12 +379,8 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
     };
 
     return MergeNestedLocations<NestedLexicalBlocksMatchKey>(
-        GetLocOrScopeMatchKey,
-        [](MDNode *L) -> bool { return L; },
-        NextScopeWithDifferentFile,
-        MergeTextualInclusions,
-        [](MDNode *N) { return nullptr; },
-        L1, L2);
+        GetLocOrScopeMatchKey, IsChainOver, NextScopeWithDifferentFile,
+        MergeTextualInclusions, [](MDNode *N) { return nullptr; }, L1, L2);
   };
 
   // When traversing inlinedAt chain, we consider locations within the same
@@ -395,12 +396,9 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
     return L->getInlinedAt();
   };
   return MergeNestedLocations<InlinedAtLookupKey>(
-      GetInlinedAtKey,
-      [](MDNode *N) { return N; },
-      NextInlinedAt,
-      MergeInlinedLocations,
-      [GetInlinedAtKey](MDNode *N) { return GetInlinedAtKey(N).second; },
-      LocA, LocB);
+      GetInlinedAtKey, IsChainOver, NextInlinedAt, MergeInlinedLocations,
+      [GetInlinedAtKey](MDNode *N) { return GetInlinedAtKey(N).second; }, LocA,
+      LocB);
 }
 
 std::optional<unsigned>

>From 21dd010e63de06b9dd1ec511d28ac0f97564de3a Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Wed, 12 Feb 2025 16:35:55 +0100
Subject: [PATCH 3/5] Fix lookup key for textual inclusion merge

---
 llvm/lib/IR/DebugInfoMetadata.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 359721c215c05..525c1e42e260b 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -269,10 +269,6 @@ static DILocalScope *NextScopeWithDifferentFile(MDNode *LocOrScope) {
     S = S->getScope();
   }
 
-  if (auto *SP = dyn_cast_or_null<DISubprogram>(S)) {
-    return SP;
-  }
-
   return nullptr;
 }
 
@@ -325,13 +321,13 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
 
     // We should traverse DILexicalBlock's that enclose input locations and
     // have different file attribute.
-    using NestedLexicalBlocksMatchKey = MDNode *;
+    using NestedLexicalBlocksMatchKey = DIFile *;
     // We try to match equal scopes or locations belonging to the same file.
     auto GetLocOrScopeMatchKey = [](MDNode *N) -> NestedLexicalBlocksMatchKey {
       if (auto *Loc = dyn_cast_if_present<DILocation>(N)) {
         return Loc->getFile();
       } else if (auto *LS = dyn_cast_if_present<DILocalScope>(N)) {
-        return LS;
+        return LS->getFile();
       }
 
       return nullptr;

>From 376e62934cc619e636ee85296a198b433c44bab0 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Wed, 12 Feb 2025 16:42:40 +0100
Subject: [PATCH 4/5] Fix comment

---
 llvm/lib/IR/DebugInfoMetadata.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 525c1e42e260b..d2e4c7415e557 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -243,8 +243,8 @@ static DIScope *GetNearestCommonScope(DILocation *L1, DILocation *L2) {
   return nullptr;
 }
 
-/// Walk up the chain of parent local scopes until the file has changed
-/// or DISubprogram reached.
+/// Returns next parent scope having different file within DISubprogram
+/// or nullptr.
 static DILocalScope *NextScopeWithDifferentFile(MDNode *LocOrScope) {
   assert((isa<DILocation>(LocOrScope) || isa<DILocalScope>(LocOrScope)) &&
          "DILocation or DILocalScope expected");

>From 2ac90345125918f2f84766702a1175e97b703e8d Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Wed, 12 Feb 2025 17:22:32 +0100
Subject: [PATCH 5/5] Fix matching DILexicalBlock

---
 llvm/lib/IR/DebugInfoMetadata.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index d2e4c7415e557..41a675d2f287d 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -226,6 +226,17 @@ static DIScope *GetNearestCommonScope(DILocation *L1, DILocation *L2) {
     }
   }
 
+  // Try matching DILexicalBlocks enclosing L1, L2.
+  if (auto *LB1 = dyn_cast<DILexicalBlock>(S1)) {
+    if (auto *LB2 = dyn_cast<DILexicalBlock>(S2)) {
+      if (LB1->getFile() && LB1->getFile() == LB2->getFile() &&
+          LB1->getLine() == LB2->getLine() &&
+          LB1->getColumn() == LB2->getColumn()) {
+        return LB1;
+      }
+    }
+  }
+
   SmallPtrSet<DIScope *, 8> Scopes;
   for (; S1; S1 = S1->getScope()) {
     Scopes.insert(S1);



More information about the llvm-commits mailing list