[llvm] 12a7aea - [DebugInfo] Merge partially matching chains of inlined locations

David Stenberg via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 6 05:37:21 PST 2023


Author: David Stenberg
Date: 2023-03-06T14:23:29+01:00
New Revision: 12a7aea6b02288000ba6f5c477284b26df8dca01

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

LOG: [DebugInfo] Merge partially matching chains of inlined locations

For example, if you have a chain of inlined funtions like this:

   1 #include <stdlib.h>
   2 int g1 = 4, g2 = 6;
   3
   4 static inline void bar(int q) {
   5   if (q > 5)
   6     abort();
   7 }
   8
   9 static inline void foo(int q) {
  10   bar(q);
  11 }
  12
  13 int main() {
  14   foo(g1);
  15   foo(g2);
  16   return 0;
  17 }

with optimizations you could end up with a single abort call for the two
inlined instances of foo(). When merging the locations for those inlined
instances you would previously end up with a 0:0 location in main().
Leaving out that inlined chain from the location for the abort call
could make troubleshooting difficult in some cases.

This patch changes DILocation::getMergedLocation() to try to handle such
cases. The function is rewritten to first find a common starting point
for the two locations (same subprogram and inlined-at location), and
then in reverse traverses the inlined-at chain looking for matches in
each subprogram. For each subprogram, the merge function will find the
nearest common scope for the two locations, and matching line and
column (or set them to 0 if not matching).

In the example above, you will for the abort call get a location in
bar() at 6:5, inlined in foo() at 10:3, inlined in main() at 0:0 (since
the two inlined functions are on different lines, but in the same
scope).

I have not seen anything in the DWARF standard that would disallow
inlining a non-zero location at 0:0 in the inlined-at function, and both
LLDB and GDB seem to accept these locations (with D142552 needed for
LLDB to handle cases where the file, line and column number are all 0).
One incompatibility with GDB is that it seems to ignore 0-line locations
in some cases, but I am not aware of any specific issue that this patch
produces related to that.

With x86-64 LLDB (trunk) you previously got:

  frame #0: 0x00007ffff7a44930 libc.so.6`abort
  frame #1: 0x00005555555546ec a.out`main at merge.c:0

and will now get:

  frame #0: 0x[...] libc.so.6`abort
  frame #1: 0x[...] a.out`main [inlined] bar(q=<unavailable>) at merge.c:6:5
  frame #2: 0x[...] a.out`main [inlined] foo(q=<unavailable>) at merge.c:10:3
  frame #3: 0x[...] a.out`main at merge.c:0

and with x86-64 GDB (11.1) you will get:

  (gdb) bt
  #0  0x00007ffff7a44930 in abort () from /lib64/libc.so.6
  #1  0x00005555555546ec in bar (q=<optimized out>) at merge.c:6
  #2  foo (q=<optimized out>) at merge.c:10
  #3  0x00005555555546ec in main ()

Reviewed By: aprantl, dblaikie

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

Added: 
    llvm/test/DebugInfo/MIR/X86/merge-inline-loc1.mir
    llvm/test/DebugInfo/MIR/X86/merge-inline-loc2.mir
    llvm/test/DebugInfo/MIR/X86/merge-inline-loc3.mir
    llvm/test/DebugInfo/MIR/X86/merge-inline-loc4.mir

Modified: 
    llvm/lib/IR/DebugInfoMetadata.cpp
    llvm/test/DebugInfo/X86/merge_inlined_loc.ll
    llvm/unittests/IR/MetadataTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index ad9540e51932..a5d931769573 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -13,6 +13,7 @@
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "LLVMContextImpl.h"
 #include "MetadataImpl.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/BinaryFormat/Dwarf.h"
@@ -114,63 +115,122 @@ const DILocation *DILocation::getMergedLocation(const DILocation *LocA,
     return LocA;
 
   LLVMContext &C = LocA->getContext();
-  SmallDenseMap<std::pair<DILocalScope *, DILocation *>,
-                std::pair<unsigned, unsigned>, 4>
-      Locations;
-
-  DIScope *S = LocA->getScope();
-  DILocation *L = LocA->getInlinedAt();
-  unsigned Line = LocA->getLine();
-  unsigned Col = LocA->getColumn();
-
-  // Walk from the current source locaiton until the file scope;
-  // then, do the same for the inlined-at locations.
-  auto AdvanceToParentLoc = [&S, &L, &Line, &Col]() {
-    S = S->getScope();
-    if (!S && L) {
-      Line = L->getLine();
-      Col = L->getColumn();
-      S = L->getScope();
-      L = L->getInlinedAt();
-    }
-  };
 
-  while (S) {
-    if (auto *LS = dyn_cast<DILocalScope>(S))
-      Locations.try_emplace(std::make_pair(LS, L), std::make_pair(Line, Col));
-    AdvanceToParentLoc();
+  using LocVec = SmallVector<const DILocation *>;
+  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
+  // a matching starting location in LocB's chain.
+  for (auto [L, I] = std::make_pair(LocA, 0U); L; L = L->getInlinedAt(), 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?");
+    (void)Res;
   }
 
-  // Walk the source locations of LocB until a match with LocA is found.
-  S = LocB->getScope();
-  L = LocB->getInlinedAt();
-  Line = LocB->getLine();
-  Col = LocB->getColumn();
-  while (S) {
-    if (auto *LS = dyn_cast<DILocalScope>(S)) {
-      auto MatchLoc = Locations.find(std::make_pair(LS, L));
-      if (MatchLoc != Locations.end()) {
-        // If the lines match, keep the line, but set the column to '0'
-        // If the lines don't match, pick a "line 0" location but keep
-        // the current scope and inlined-at.
-        bool SameLine = Line == MatchLoc->second.first;
-        bool SameCol = Col == MatchLoc->second.second;
-        Line = SameLine ? Line : 0;
-        Col = SameLine && SameCol ? Col : 0;
-        break;
-      }
-    }
-    AdvanceToParentLoc();
+  LocVec::reverse_iterator ARIt = ALocs.rend();
+  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
+  // not need to look at those parts of the chains.
+  for (auto [L, I] = std::make_pair(LocB, 0U); L; L = L->getInlinedAt(), 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()});
+    if (IT == ALookup.end())
+      continue;
+
+    // The + 1 is to account for the &*rev_it = &(it - 1) relationship.
+    ARIt = LocVec::reverse_iterator(ALocs.begin() + IT->second + 1);
+    BRIt = LocVec::reverse_iterator(BLocs.begin() + I + 1);
+
+    // If we have found a matching starting location we do not need to add more
+    // locations to BLocs, since we will only look at location pairs preceding
+    // the matching starting location, and adding more elements to BLocs could
+    // invalidate the iterator that we initialized here.
+    break;
   }
 
-  if (!S) {
-    // If the two locations are irreconsilable, pick any scope,
-    // and return a "line 0" location.
-    Line = Col = 0;
-    S = LocA->getScope();
+  // 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 * {
+    if (L1 == L2)
+      return DILocation::get(C, L1->getLine(), L1->getColumn(), L1->getScope(),
+                             InlinedAt);
+
+    // If the locations originate from 
diff erent subprograms we can't produce
+    // a common location.
+    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;
+      }
+
+      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);
+  };
+
+  DILocation *Result = ARIt != ALocs.rend() ? (*ARIt)->getInlinedAt() : nullptr;
+
+  // 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 (!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;
+
+    Result = Tmp;
   }
 
-  return DILocation::get(C, Line, Col, S, L);
+  if (Result)
+    return Result;
+
+  // 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);
 }
 
 std::optional<unsigned>

diff  --git a/llvm/test/DebugInfo/MIR/X86/merge-inline-loc1.mir b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc1.mir
new file mode 100644
index 000000000000..1504789cd585
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc1.mir
@@ -0,0 +1,222 @@
+# RUN: llc -mtriple=x86_64-pc-linux %s -run-pass=branch-folder -o - | FileCheck %s
+
+--- |
+  ; ModuleID = 'case1.c'
+  source_filename = "case1.c"
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+
+  @q1 = dso_local local_unnamed_addr global i32 1, align 4
+  @q2 = dso_local local_unnamed_addr global i32 4, align 4
+  @q3 = dso_local local_unnamed_addr global i32 2, align 4
+  @g1 = dso_local local_unnamed_addr global i32 0, align 4
+  @g2 = dso_local local_unnamed_addr global i32 0, align 4
+  @g3 = dso_local local_unnamed_addr global i32 0, align 4
+
+  ; Function Attrs: nounwind uwtable
+  define dso_local i32 @multiple_inl_one_loc() local_unnamed_addr #0 !dbg !9 {
+  entry:
+    %0 = load i32, ptr @q1, align 4, !dbg !12, !tbaa !13
+    %cmp.i = icmp sgt i32 %0, 3, !dbg !17
+    br i1 %cmp.i, label %if.then.i, label %inl1.exit, !dbg !20
+
+  if.then.i:                                        ; preds = %entry
+    tail call void @abort() #2, !dbg !21
+    unreachable, !dbg !21
+
+  inl1.exit:                                        ; preds = %entry
+    %mul.i = mul nsw i32 %0, 152, !dbg !22
+    %add.i = add nsw i32 %mul.i, 100, !dbg !23
+    store i32 %add.i, ptr @g1, align 4, !dbg !24, !tbaa !13
+    %1 = load i32, ptr @q2, align 4, !dbg !25, !tbaa !13
+    %cmp.i3 = icmp sgt i32 %1, 3, !dbg !26
+    br i1 %cmp.i3, label %if.then.i4, label %inl1.exit7, !dbg !28
+
+  if.then.i4:                                       ; preds = %inl1.exit
+    tail call void @abort() #2, !dbg !29
+    unreachable, !dbg !29
+
+  inl1.exit7:                                       ; preds = %inl1.exit
+    %mul.i5 = mul nsw i32 %1, 152, !dbg !30
+    %add.i6 = add nsw i32 %mul.i5, 200, !dbg !31
+    store i32 %add.i6, ptr @g2, align 4, !dbg !32, !tbaa !13
+    %2 = load i32, ptr @q3, align 4, !dbg !33, !tbaa !13
+    %cmp.i8 = icmp sgt i32 %2, 3, !dbg !34
+    br i1 %cmp.i8, label %if.then.i9, label %inl1.exit12, !dbg !36
+
+  if.then.i9:                                       ; preds = %inl1.exit7
+    tail call void @abort() #2, !dbg !37
+    unreachable, !dbg !37
+
+  inl1.exit12:                                      ; preds = %inl1.exit7
+    %mul.i10 = mul nsw i32 %2, 152, !dbg !38
+    %add.i11 = add nsw i32 %mul.i10, 300, !dbg !39
+    store i32 %add.i11, ptr @q3, align 4, !dbg !40, !tbaa !13
+    ret i32 0, !dbg !41
+  }
+
+  ; Function Attrs: noreturn nounwind
+  declare !dbg !42 void @abort() local_unnamed_addr #1
+
+  attributes #0 = { nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #1 = { noreturn nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #2 = { noreturn 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 16.0.0.prerel", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+  !1 = !DIFile(filename: "case1.c", directory: "/", checksumkind: CSK_MD5, checksum: "36d7da9616644fc8b011f5c49108ab31")
+  !2 = !{i32 7, !"Dwarf Version", i32 5}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = !{i32 1, !"wchar_size", i32 4}
+  !5 = !{i32 8, !"PIC Level", i32 2}
+  !6 = !{i32 7, !"PIE Level", i32 2}
+  !7 = !{i32 7, !"uwtable", i32 2}
+  !8 = !{!"clang version 16.0.0.prerel"}
+  !9 = distinct !DISubprogram(name: "multiple_inl_one_loc", scope: !1, file: !1, line: 13, type: !10, scopeLine: 14, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !10 = !DISubroutineType(types: !11)
+  !11 = !{}
+  !12 = !DILocation(line: 15, column: 13, scope: !9)
+  !13 = !{!14, !14, i64 0}
+  !14 = !{!"int", !15, i64 0}
+  !15 = !{!"omnipotent char", !16, i64 0}
+  !16 = !{!"Simple C/C++ TBAA"}
+  !17 = !DILocation(line: 5, column: 9, scope: !18, inlinedAt: !19)
+  !18 = distinct !DISubprogram(name: "inl1", scope: !1, file: !1, line: 3, type: !10, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !19 = distinct !DILocation(line: 15, column: 8, scope: !9)
+  !20 = !DILocation(line: 5, column: 7, scope: !18, inlinedAt: !19)
+  !21 = !DILocation(line: 6, column: 5, scope: !18, inlinedAt: !19)
+  !22 = !DILocation(line: 7, column: 12, scope: !18, inlinedAt: !19)
+  !23 = !DILocation(line: 7, column: 18, scope: !18, inlinedAt: !19)
+  !24 = !DILocation(line: 15, column: 6, scope: !9)
+  !25 = !DILocation(line: 16, column: 13, scope: !9)
+  !26 = !DILocation(line: 5, column: 9, scope: !18, inlinedAt: !27)
+  !27 = distinct !DILocation(line: 16, column: 8, scope: !9)
+  !28 = !DILocation(line: 5, column: 7, scope: !18, inlinedAt: !27)
+  !29 = !DILocation(line: 6, column: 5, scope: !18, inlinedAt: !27)
+  !30 = !DILocation(line: 7, column: 12, scope: !18, inlinedAt: !27)
+  !31 = !DILocation(line: 7, column: 18, scope: !18, inlinedAt: !27)
+  !32 = !DILocation(line: 16, column: 6, scope: !9)
+  !33 = !DILocation(line: 17, column: 13, scope: !9)
+  !34 = !DILocation(line: 5, column: 9, scope: !18, inlinedAt: !35)
+  !35 = distinct !DILocation(line: 17, column: 8, scope: !9)
+  !36 = !DILocation(line: 5, column: 7, scope: !18, inlinedAt: !35)
+  !37 = !DILocation(line: 6, column: 5, scope: !18, inlinedAt: !35)
+  !38 = !DILocation(line: 7, column: 12, scope: !18, inlinedAt: !35)
+  !39 = !DILocation(line: 7, column: 18, scope: !18, inlinedAt: !35)
+  !40 = !DILocation(line: 17, column: 6, scope: !9)
+  !41 = !DILocation(line: 18, column: 3, scope: !9)
+  !42 = !DISubprogram(name: "abort", scope: !43, file: !43, line: 514, type: !10, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !11)
+  !43 = !DIFile(filename: "/usr/include/stdlib.h", directory: "", checksumkind: CSK_MD5, checksum: "f7a1412d75d9e3df251dfc21b02d59ef")
+
+...
+---
+name:            multiple_inl_one_loc
+alignment:       16
+tracksRegLiveness: true
+tracksDebugUserValues: true
+frameInfo:
+  stackSize:       8
+  offsetAdjustment: -8
+  maxAlignment:    1
+  adjustsStack:    true
+  hasCalls:        true
+  maxCallFrameSize: 0
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x00000800), %bb.2(0x7ffff800)
+
+    frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q1, $noreg, debug-location !12 :: (dereferenceable load (s32) from @q1, !tbaa !13)
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !17
+    JCC_1 %bb.2, 12, implicit killed $eflags, debug-location !20
+    JMP_1 %bb.1, debug-location !20
+
+  bb.1.if.then.i:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !21
+
+  bb.2.inl1.exit:
+    successors: %bb.3(0x00000800), %bb.4(0x7ffff800)
+    liveins: $eax
+
+    renamable $eax = nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !22
+    renamable $eax = nsw ADD32ri8 killed renamable $eax, 100, implicit-def dead $eflags, debug-location !23
+    MOV32mr $rip, 1, $noreg, @g1, $noreg, killed renamable $eax, debug-location !24 :: (store (s32) into @g1, !tbaa !13)
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q2, $noreg, debug-location !25 :: (dereferenceable load (s32) from @q2, !tbaa !13)
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !26
+    JCC_1 %bb.4, 12, implicit killed $eflags, debug-location !28
+    JMP_1 %bb.3, debug-location !28
+
+  bb.3.if.then.i4:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !29
+
+  bb.4.inl1.exit7:
+    successors: %bb.5(0x00000800), %bb.6(0x7ffff800)
+    liveins: $eax
+
+    renamable $eax = nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !30
+    renamable $eax = nsw ADD32ri killed renamable $eax, 200, implicit-def dead $eflags, debug-location !31
+    MOV32mr $rip, 1, $noreg, @g2, $noreg, killed renamable $eax, debug-location !32 :: (store (s32) into @g2, !tbaa !13)
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q3, $noreg, debug-location !33 :: (dereferenceable load (s32) from @q3, !tbaa !13)
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !34
+    JCC_1 %bb.6, 12, implicit killed $eflags, debug-location !36
+    JMP_1 %bb.5, debug-location !36
+
+  bb.5.if.then.i9:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !37
+
+  bb.6.inl1.exit12:
+    liveins: $eax
+
+    renamable $eax = nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !38
+    renamable $eax = nsw ADD32ri killed renamable $eax, 300, implicit-def dead $eflags, debug-location !39
+    MOV32mr $rip, 1, $noreg, @q3, $noreg, killed renamable $eax, debug-location !40 :: (store (s32) into @q3, !tbaa !13)
+    $eax = MOV32r0 implicit-def dead $eflags, debug-location !41
+    $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !41
+    frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !41
+    RET 0, $eax, debug-location !41
+
+...
+
+# In this case we have a abort call block folded from one single location in
+# three inlined instances of inl1():
+#
+#  1 | #include <stdlib.h>
+#  2 |
+#  3 | static inline int inl1(int q, int n)
+#  4 | {
+#  5 |   if (q > 3)
+#  6 |     abort();
+#  7 |   return q * 152 + n;
+#  8 | }
+#  9 |
+# 10 | int q1 = 1, q2 = 4, q3 = 2;
+# 11 | int g1, g2, g3;
+# 12 |
+# 13 | int multiple_inl_one_loc()
+# 14 | {
+# 15 |   g1 = inl1(q1, 100);
+# 16 |   g2 = inl1(q2, 200);
+# 17 |   q3 = inl1(q3, 300);
+# 18 |   return 0;
+# 19 | }
+#
+# We should produce a merged location describing that the abort call is located
+# at line 6 in inl1() inlined at line 0.
+
+# CHECK-DAG: [[INLINER:![0-9]+]] = distinct !DISubprogram(name: "multiple_inl_one_loc"
+# CHECK-DAG: [[INLINEE:![0-9]+]] = distinct !DISubprogram(name: "inl1"
+
+# CHECK-NOT: CALL64pcrel32
+# CHECK: CALL64pcrel32 target-flags(x86-plt) @abort, {{.*}} debug-location !DILocation(line: 6, column: 5, scope: [[INLINEE]], inlinedAt: !DILocation(line: 0, scope: [[INLINER]]))
+# CHECK-NOT: CALL64pcrel32

diff  --git a/llvm/test/DebugInfo/MIR/X86/merge-inline-loc2.mir b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc2.mir
new file mode 100644
index 000000000000..0564b49fcafd
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc2.mir
@@ -0,0 +1,339 @@
+# RUN: llc -mtriple=x86_64-pc-linux %s -run-pass=branch-folder -o - | FileCheck %s
+
+--- |
+  ; ModuleID = 'case2.c'
+  source_filename = "case2.c"
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+
+  @q1 = dso_local local_unnamed_addr global i32 1, align 4, !dbg !0
+  @q2 = dso_local local_unnamed_addr global i32 4, align 4, !dbg !5
+  @q3 = dso_local local_unnamed_addr global i32 2, align 4, !dbg !8
+  @g1 = dso_local local_unnamed_addr global i32 0, align 4, !dbg !10
+  @g2 = dso_local local_unnamed_addr global i32 0, align 4, !dbg !12
+  @g3 = dso_local local_unnamed_addr global i32 0, align 4, !dbg !14
+
+  ; Function Attrs: nounwind uwtable
+  define dso_local i32 @multiple_inl_multiple_loc() local_unnamed_addr #0 !dbg !23 {
+  entry:
+    %0 = load i32, ptr @q1, align 4, !dbg !27, !tbaa !28
+    call void @llvm.dbg.value(metadata i32 %0, metadata !32, metadata !DIExpression()), !dbg !38
+    call void @llvm.dbg.value(metadata i32 100, metadata !37, metadata !DIExpression()), !dbg !38
+    %cmp.i = icmp sgt i32 %0, 3, !dbg !40
+    br i1 %cmp.i, label %if.then.i, label %if.end.i, !dbg !42
+
+  if.then.i:                                        ; preds = %entry
+    tail call void @abort() #3, !dbg !43
+    unreachable, !dbg !43
+
+  if.end.i:                                         ; preds = %entry
+    %cmp1.i = icmp slt i32 %0, 1, !dbg !44
+    br i1 %cmp1.i, label %if.then2.i, label %inl2.exit, !dbg !46
+
+  if.then2.i:                                       ; preds = %if.end.i
+    tail call void @abort() #3, !dbg !47
+    unreachable, !dbg !47
+
+  inl2.exit:                                        ; preds = %if.end.i
+    %mul.i = mul nuw nsw i32 %0, 152, !dbg !48
+    %add.i = add nuw nsw i32 %mul.i, 100, !dbg !49
+    store i32 %add.i, ptr @g1, align 4, !dbg !50, !tbaa !28
+    %1 = load i32, ptr @q2, align 4, !dbg !51, !tbaa !28
+    call void @llvm.dbg.value(metadata i32 %1, metadata !32, metadata !DIExpression()), !dbg !52
+    call void @llvm.dbg.value(metadata i32 200, metadata !37, metadata !DIExpression()), !dbg !52
+    %cmp.i3 = icmp sgt i32 %1, 3, !dbg !54
+    br i1 %cmp.i3, label %if.then.i4, label %if.end.i6, !dbg !55
+
+  if.then.i4:                                       ; preds = %inl2.exit
+    tail call void @abort() #3, !dbg !56
+    unreachable, !dbg !56
+
+  if.end.i6:                                        ; preds = %inl2.exit
+    %cmp1.i5 = icmp slt i32 %1, 1, !dbg !57
+    br i1 %cmp1.i5, label %if.then2.i7, label %inl2.exit10, !dbg !58
+
+  if.then2.i7:                                      ; preds = %if.end.i6
+    tail call void @abort() #3, !dbg !59
+    unreachable, !dbg !59
+
+  inl2.exit10:                                      ; preds = %if.end.i6
+    %mul.i8 = mul nuw nsw i32 %1, 152, !dbg !60
+    %add.i9 = add nuw nsw i32 %mul.i8, 200, !dbg !61
+    store i32 %add.i9, ptr @g2, align 4, !dbg !62, !tbaa !28
+    %2 = load i32, ptr @q3, align 4, !dbg !63, !tbaa !28
+    call void @llvm.dbg.value(metadata i32 %2, metadata !32, metadata !DIExpression()), !dbg !64
+    call void @llvm.dbg.value(metadata i32 300, metadata !37, metadata !DIExpression()), !dbg !64
+    %cmp.i11 = icmp sgt i32 %2, 3, !dbg !66
+    br i1 %cmp.i11, label %if.then.i12, label %if.end.i14, !dbg !67
+
+  if.then.i12:                                      ; preds = %inl2.exit10
+    tail call void @abort() #3, !dbg !68
+    unreachable, !dbg !68
+
+  if.end.i14:                                       ; preds = %inl2.exit10
+    %cmp1.i13 = icmp slt i32 %2, 1, !dbg !69
+    br i1 %cmp1.i13, label %if.then2.i15, label %inl2.exit18, !dbg !70
+
+  if.then2.i15:                                     ; preds = %if.end.i14
+    tail call void @abort() #3, !dbg !71
+    unreachable, !dbg !71
+
+  inl2.exit18:                                      ; preds = %if.end.i14
+    %mul.i16 = mul nuw nsw i32 %2, 152, !dbg !72
+    %add.i17 = add nuw nsw i32 %mul.i16, 300, !dbg !73
+    store i32 %add.i17, ptr @q3, align 4, !dbg !74, !tbaa !28
+    ret i32 0, !dbg !75
+  }
+
+  ; Function Attrs: noreturn nounwind
+  declare !dbg !76 void @abort() local_unnamed_addr #1
+
+  ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+  declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+  attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #1 = { noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+  attributes #3 = { noreturn nounwind }
+
+  !llvm.dbg.cu = !{!2}
+  !llvm.module.flags = !{!16, !17, !18, !19, !20, !21}
+  !llvm.ident = !{!22}
+
+  !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+  !1 = distinct !DIGlobalVariable(name: "q1", scope: !2, file: !3, line: 12, type: !7, isLocal: false, isDefinition: true)
+  !2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 16.0.0.prerel", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+  !3 = !DIFile(filename: "case2.c", directory: "/", checksumkind: CSK_MD5, checksum: "5093c50294e57eaa7f9ed00bfd62075c")
+  !4 = !{!0, !5, !8, !10, !12, !14}
+  !5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+  !6 = distinct !DIGlobalVariable(name: "q2", scope: !2, file: !3, line: 12, type: !7, isLocal: false, isDefinition: true)
+  !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression())
+  !9 = distinct !DIGlobalVariable(name: "q3", scope: !2, file: !3, line: 12, type: !7, isLocal: false, isDefinition: true)
+  !10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
+  !11 = distinct !DIGlobalVariable(name: "g1", scope: !2, file: !3, line: 13, type: !7, isLocal: false, isDefinition: true)
+  !12 = !DIGlobalVariableExpression(var: !13, expr: !DIExpression())
+  !13 = distinct !DIGlobalVariable(name: "g2", scope: !2, file: !3, line: 13, type: !7, isLocal: false, isDefinition: true)
+  !14 = !DIGlobalVariableExpression(var: !15, expr: !DIExpression())
+  !15 = distinct !DIGlobalVariable(name: "g3", scope: !2, file: !3, line: 13, type: !7, isLocal: false, isDefinition: true)
+  !16 = !{i32 7, !"Dwarf Version", i32 5}
+  !17 = !{i32 2, !"Debug Info Version", i32 3}
+  !18 = !{i32 1, !"wchar_size", i32 4}
+  !19 = !{i32 8, !"PIC Level", i32 2}
+  !20 = !{i32 7, !"PIE Level", i32 2}
+  !21 = !{i32 7, !"uwtable", i32 2}
+  !22 = !{!"clang version 16.0.0.prerel"}
+  !23 = distinct !DISubprogram(name: "multiple_inl_multiple_loc", scope: !3, file: !3, line: 15, type: !24, scopeLine: 16, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !26)
+  !24 = !DISubroutineType(types: !25)
+  !25 = !{!7}
+  !26 = !{}
+  !27 = !DILocation(line: 17, column: 13, scope: !23)
+  !28 = !{!29, !29, i64 0}
+  !29 = !{!"int", !30, i64 0}
+  !30 = !{!"omnipotent char", !31, i64 0}
+  !31 = !{!"Simple C/C++ TBAA"}
+  !32 = !DILocalVariable(name: "q", arg: 1, scope: !33, file: !3, line: 3, type: !7)
+  !33 = distinct !DISubprogram(name: "inl2", scope: !3, file: !3, line: 3, type: !34, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !36)
+  !34 = !DISubroutineType(types: !35)
+  !35 = !{!7, !7, !7}
+  !36 = !{!32, !37}
+  !37 = !DILocalVariable(name: "n", arg: 2, scope: !33, file: !3, line: 3, type: !7)
+  !38 = !DILocation(line: 0, scope: !33, inlinedAt: !39)
+  !39 = distinct !DILocation(line: 17, column: 8, scope: !23)
+  !40 = !DILocation(line: 5, column: 9, scope: !41, inlinedAt: !39)
+  !41 = distinct !DILexicalBlock(scope: !33, file: !3, line: 5, column: 7)
+  !42 = !DILocation(line: 5, column: 7, scope: !33, inlinedAt: !39)
+  !43 = !DILocation(line: 6, column: 5, scope: !41, inlinedAt: !39)
+  !44 = !DILocation(line: 7, column: 9, scope: !45, inlinedAt: !39)
+  !45 = distinct !DILexicalBlock(scope: !33, file: !3, line: 7, column: 7)
+  !46 = !DILocation(line: 7, column: 7, scope: !33, inlinedAt: !39)
+  !47 = !DILocation(line: 8, column: 5, scope: !45, inlinedAt: !39)
+  !48 = !DILocation(line: 9, column: 12, scope: !33, inlinedAt: !39)
+  !49 = !DILocation(line: 9, column: 18, scope: !33, inlinedAt: !39)
+  !50 = !DILocation(line: 17, column: 6, scope: !23)
+  !51 = !DILocation(line: 18, column: 13, scope: !23)
+  !52 = !DILocation(line: 0, scope: !33, inlinedAt: !53)
+  !53 = distinct !DILocation(line: 18, column: 8, scope: !23)
+  !54 = !DILocation(line: 5, column: 9, scope: !41, inlinedAt: !53)
+  !55 = !DILocation(line: 5, column: 7, scope: !33, inlinedAt: !53)
+  !56 = !DILocation(line: 6, column: 5, scope: !41, inlinedAt: !53)
+  !57 = !DILocation(line: 7, column: 9, scope: !45, inlinedAt: !53)
+  !58 = !DILocation(line: 7, column: 7, scope: !33, inlinedAt: !53)
+  !59 = !DILocation(line: 8, column: 5, scope: !45, inlinedAt: !53)
+  !60 = !DILocation(line: 9, column: 12, scope: !33, inlinedAt: !53)
+  !61 = !DILocation(line: 9, column: 18, scope: !33, inlinedAt: !53)
+  !62 = !DILocation(line: 18, column: 6, scope: !23)
+  !63 = !DILocation(line: 19, column: 13, scope: !23)
+  !64 = !DILocation(line: 0, scope: !33, inlinedAt: !65)
+  !65 = distinct !DILocation(line: 19, column: 8, scope: !23)
+  !66 = !DILocation(line: 5, column: 9, scope: !41, inlinedAt: !65)
+  !67 = !DILocation(line: 5, column: 7, scope: !33, inlinedAt: !65)
+  !68 = !DILocation(line: 6, column: 5, scope: !41, inlinedAt: !65)
+  !69 = !DILocation(line: 7, column: 9, scope: !45, inlinedAt: !65)
+  !70 = !DILocation(line: 7, column: 7, scope: !33, inlinedAt: !65)
+  !71 = !DILocation(line: 8, column: 5, scope: !45, inlinedAt: !65)
+  !72 = !DILocation(line: 9, column: 12, scope: !33, inlinedAt: !65)
+  !73 = !DILocation(line: 9, column: 18, scope: !33, inlinedAt: !65)
+  !74 = !DILocation(line: 19, column: 6, scope: !23)
+  !75 = !DILocation(line: 20, column: 3, scope: !23)
+  !76 = !DISubprogram(name: "abort", scope: !77, file: !77, line: 514, type: !78, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: DISPFlagOptimized, retainedNodes: !26)
+  !77 = !DIFile(filename: "/usr/include/stdlib.h", directory: "", checksumkind: CSK_MD5, checksum: "f7a1412d75d9e3df251dfc21b02d59ef")
+  !78 = !DISubroutineType(types: !79)
+  !79 = !{null}
+
+...
+---
+name:            multiple_inl_multiple_loc
+alignment:       16
+tracksRegLiveness: true
+tracksDebugUserValues: true
+frameInfo:
+  stackSize:       8
+  offsetAdjustment: -8
+  maxAlignment:    1
+  adjustsStack:    true
+  hasCalls:        true
+  maxCallFrameSize: 0
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x00000800), %bb.2(0x7ffff800)
+
+    frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q1, $noreg, debug-instr-number 1, debug-location !27 :: (dereferenceable load (s32) from @q1, !tbaa !28)
+    DBG_INSTR_REF !32, !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(1, 0), debug-location !38
+    DBG_VALUE 100, $noreg, !37, !DIExpression(), debug-location !38
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !40
+    JCC_1 %bb.2, 12, implicit killed $eflags, debug-location !42
+    JMP_1 %bb.1, debug-location !42
+
+  bb.1.if.then.i:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !43
+
+  bb.2.if.end.i:
+    successors: %bb.3(0x00000800), %bb.4(0x7ffff800)
+    liveins: $eax
+
+    TEST32rr renamable $eax, renamable $eax, implicit-def $eflags, debug-location !44
+    JCC_1 %bb.4, 15, implicit killed $eflags, debug-location !46
+    JMP_1 %bb.3, debug-location !46
+
+  bb.3.if.then2.i:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !47
+
+  bb.4.inl2.exit:
+    successors: %bb.5(0x00000800), %bb.6(0x7ffff800)
+    liveins: $eax
+
+    renamable $eax = nuw nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !48
+    renamable $eax = nuw nsw ADD32ri8 killed renamable $eax, 100, implicit-def dead $eflags, debug-location !49
+    MOV32mr $rip, 1, $noreg, @g1, $noreg, killed renamable $eax, debug-location !50 :: (store (s32) into @g1, !tbaa !28)
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q2, $noreg, debug-instr-number 2, debug-location !51 :: (dereferenceable load (s32) from @q2, !tbaa !28)
+    DBG_INSTR_REF !32, !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(2, 0), debug-location !52
+    DBG_VALUE 200, $noreg, !37, !DIExpression(), debug-location !52
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !54
+    JCC_1 %bb.6, 12, implicit killed $eflags, debug-location !55
+    JMP_1 %bb.5, debug-location !55
+
+  bb.5.if.then.i4:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !56
+
+  bb.6.if.end.i6:
+    successors: %bb.7(0x00000800), %bb.8(0x7ffff800)
+    liveins: $eax
+
+    TEST32rr renamable $eax, renamable $eax, implicit-def $eflags, debug-location !57
+    JCC_1 %bb.8, 15, implicit killed $eflags, debug-location !58
+    JMP_1 %bb.7, debug-location !58
+
+  bb.7.if.then2.i7:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !59
+
+  bb.8.inl2.exit10:
+    successors: %bb.9(0x00000800), %bb.10(0x7ffff800)
+    liveins: $eax
+
+    renamable $eax = nuw nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !60
+    renamable $eax = nuw nsw ADD32ri killed renamable $eax, 200, implicit-def dead $eflags, debug-location !61
+    MOV32mr $rip, 1, $noreg, @g2, $noreg, killed renamable $eax, debug-location !62 :: (store (s32) into @g2, !tbaa !28)
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q3, $noreg, debug-instr-number 3, debug-location !63 :: (dereferenceable load (s32) from @q3, !tbaa !28)
+    DBG_INSTR_REF !32, !DIExpression(DW_OP_LLVM_arg, 0), dbg-instr-ref(3, 0), debug-location !64
+    DBG_VALUE 300, $noreg, !37, !DIExpression(), debug-location !64
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !66
+    JCC_1 %bb.10, 12, implicit killed $eflags, debug-location !67
+    JMP_1 %bb.9, debug-location !67
+
+  bb.9.if.then.i12:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !68
+
+  bb.10.if.end.i14:
+    successors: %bb.11(0x00000800), %bb.12(0x7ffff800)
+    liveins: $eax
+
+    TEST32rr renamable $eax, renamable $eax, implicit-def $eflags, debug-location !69
+    JCC_1 %bb.12, 15, implicit killed $eflags, debug-location !70
+    JMP_1 %bb.11, debug-location !70
+
+  bb.11.if.then2.i15:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !71
+
+  bb.12.inl2.exit18:
+    liveins: $eax
+
+    renamable $eax = nuw nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !72
+    renamable $eax = nuw nsw ADD32ri killed renamable $eax, 300, implicit-def dead $eflags, debug-location !73
+    MOV32mr $rip, 1, $noreg, @q3, $noreg, killed renamable $eax, debug-location !74 :: (store (s32) into @q3, !tbaa !28)
+    $eax = MOV32r0 implicit-def dead $eflags, debug-location !75
+    $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !75
+    frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !75
+    RET 0, $eax, debug-location !75
+
+...
+
+# In this case we have a abort call block folded from two locations in
+# three inlined instances of inl1():
+#
+#  1 | #include <stdlib.h>
+#  2 |
+#  3 | static inline int inl2(int q, int n)
+#  4 | {
+#  5 |   if (q > 3)
+#  6 |     abort();
+#  7 |   if (q < 1)
+#  8 |     abort();
+#  9 |   return q * 152 + n;
+# 10 | }
+# 11 |
+# 12 | int q1 = 1, q2 = 4, q3 = 2;
+# 13 | int g1, g2, g3;
+# 14 |
+# 15 | int multiple_inl_multiple_loc()
+# 16 | {
+# 17 |   g1 = inl2(q1, 100);
+# 18 |   g2 = inl2(q2, 200);
+# 19 |   q3 = inl2(q3, 300);
+# 20 |   return 0;
+# 21 | }
+#
+# We should produce a merged location describing that the abort call is located
+# at line 0 in inl2() inlined at line 0 in multiple_inl_multiple_loc().
+
+# CHECK-DAG: [[INLINER:![0-9]+]] = distinct !DISubprogram(name: "multiple_inl_multiple_loc"
+# CHECK-DAG: [[INLINEE:![0-9]+]] = distinct !DISubprogram(name: "inl2"
+
+# CHECK-NOT: CALL64pcrel32
+# CHECK: CALL64pcrel32 target-flags(x86-plt) @abort, {{.*}} debug-location !DILocation(line: 0, scope: [[INLINEE]], inlinedAt: !DILocation(line: 0, scope: [[INLINER]]))
+# CHECK-NOT: CALL64pcrel32

diff  --git a/llvm/test/DebugInfo/MIR/X86/merge-inline-loc3.mir b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc3.mir
new file mode 100644
index 000000000000..2f21c7912c72
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc3.mir
@@ -0,0 +1,187 @@
+# RUN: llc -mtriple=x86_64-pc-linux %s -run-pass=branch-folder -o - | FileCheck %s
+
+--- |
+  ; ModuleID = 'case3.c'
+  source_filename = "case3.c"
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+
+  @q1 = dso_local local_unnamed_addr global i32 1, align 4
+  @q2 = dso_local local_unnamed_addr global i32 6, align 4
+  @g1 = dso_local local_unnamed_addr global i32 0, align 4
+  @g2 = dso_local local_unnamed_addr global i32 0, align 4
+
+  ; Function Attrs: nounwind uwtable
+  define dso_local i32 @multiple_inl_funcs() local_unnamed_addr #0 !dbg !9 {
+  entry:
+    %0 = load i32, ptr @q1, align 4, !dbg !12, !tbaa !13
+    %cmp.i = icmp sgt i32 %0, 3, !dbg !17
+    br i1 %cmp.i, label %if.then.i, label %inl3.exit, !dbg !20
+
+  if.then.i:                                        ; preds = %entry
+    tail call void @abort() #2, !dbg !21
+    unreachable, !dbg !21
+
+  inl3.exit:                                        ; preds = %entry
+    %mul.i = mul nsw i32 %0, 152, !dbg !22
+    %add.i = add nsw i32 %mul.i, 100, !dbg !23
+    store i32 %add.i, ptr @g1, align 4, !dbg !24, !tbaa !13
+    %1 = load i32, ptr @q2, align 4, !dbg !25, !tbaa !13
+    %cmp.i2 = icmp sgt i32 %1, 5, !dbg !26
+    br i1 %cmp.i2, label %if.then.i3, label %inl4.exit, !dbg !29
+
+  if.then.i3:                                       ; preds = %inl3.exit
+    tail call void @abort() #2, !dbg !30
+    unreachable, !dbg !30
+
+  inl4.exit:                                        ; preds = %inl3.exit
+    %mul.i4 = mul nsw i32 %1, %1, !dbg !31
+    %add.i5 = add nuw nsw i32 %mul.i4, 200, !dbg !32
+    store i32 %add.i5, ptr @g2, align 4, !dbg !33, !tbaa !13
+    ret i32 0, !dbg !34
+  }
+
+  ; Function Attrs: noreturn nounwind
+  declare !dbg !35 void @abort() local_unnamed_addr #1
+
+  attributes #0 = { nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #1 = { noreturn nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #2 = { noreturn 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 16.0.0.prerel", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+  !1 = !DIFile(filename: "case3.c", directory: "/", checksumkind: CSK_MD5, checksum: "53e9893099480164de1f5ee265c0cf01")
+  !2 = !{i32 7, !"Dwarf Version", i32 5}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = !{i32 1, !"wchar_size", i32 4}
+  !5 = !{i32 8, !"PIC Level", i32 2}
+  !6 = !{i32 7, !"PIE Level", i32 2}
+  !7 = !{i32 7, !"uwtable", i32 2}
+  !8 = !{!"clang version 16.0.0.prerel"}
+  !9 = distinct !DISubprogram(name: "multiple_inl_funcs", scope: !1, file: !1, line: 18, type: !10, scopeLine: 19, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !10 = !DISubroutineType(types: !11)
+  !11 = !{}
+  !12 = !DILocation(line: 20, column: 13, scope: !9)
+  !13 = !{!14, !14, i64 0}
+  !14 = !{!"int", !15, i64 0}
+  !15 = !{!"omnipotent char", !16, i64 0}
+  !16 = !{!"Simple C/C++ TBAA"}
+  !17 = !DILocation(line: 4, column: 9, scope: !18, inlinedAt: !19)
+  !18 = distinct !DISubprogram(name: "inl3", scope: !1, file: !1, line: 3, type: !10, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !19 = distinct !DILocation(line: 20, column: 8, scope: !9)
+  !20 = !DILocation(line: 4, column: 7, scope: !18, inlinedAt: !19)
+  !21 = !DILocation(line: 5, column: 5, scope: !18, inlinedAt: !19)
+  !22 = !DILocation(line: 6, column: 12, scope: !18, inlinedAt: !19)
+  !23 = !DILocation(line: 6, column: 18, scope: !18, inlinedAt: !19)
+  !24 = !DILocation(line: 20, column: 6, scope: !9)
+  !25 = !DILocation(line: 21, column: 13, scope: !9)
+  !26 = !DILocation(line: 10, column: 9, scope: !27, inlinedAt: !28)
+  !27 = distinct !DISubprogram(name: "inl4", scope: !1, file: !1, line: 9, type: !10, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !28 = distinct !DILocation(line: 21, column: 8, scope: !9)
+  !29 = !DILocation(line: 10, column: 7, scope: !27, inlinedAt: !28)
+  !30 = !DILocation(line: 11, column: 5, scope: !27, inlinedAt: !28)
+  !31 = !DILocation(line: 12, column: 12, scope: !27, inlinedAt: !28)
+  !32 = !DILocation(line: 12, column: 16, scope: !27, inlinedAt: !28)
+  !33 = !DILocation(line: 21, column: 6, scope: !9)
+  !34 = !DILocation(line: 22, column: 3, scope: !9)
+  !35 = !DISubprogram(name: "abort", scope: !36, file: !36, line: 514, type: !10, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !11)
+  !36 = !DIFile(filename: "/usr/include/stdlib.h", directory: "", checksumkind: CSK_MD5, checksum: "f7a1412d75d9e3df251dfc21b02d59ef")
+
+...
+---
+name:            multiple_inl_funcs
+alignment:       16
+tracksRegLiveness: true
+tracksDebugUserValues: true
+frameInfo:
+  stackSize:       8
+  offsetAdjustment: -8
+  maxAlignment:    1
+  adjustsStack:    true
+  hasCalls:        true
+  maxCallFrameSize: 0
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x00000800), %bb.2(0x7ffff800)
+
+    frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q1, $noreg, debug-location !12 :: (dereferenceable load (s32) from @q1, !tbaa !13)
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !17
+    JCC_1 %bb.2, 12, implicit killed $eflags, debug-location !20
+    JMP_1 %bb.1, debug-location !20
+
+  bb.1.if.then.i:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !21
+
+  bb.2.inl3.exit:
+    successors: %bb.3(0x00000800), %bb.4(0x7ffff800)
+    liveins: $eax
+
+    renamable $eax = nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !22
+    renamable $eax = nsw ADD32ri8 killed renamable $eax, 100, implicit-def dead $eflags, debug-location !23
+    MOV32mr $rip, 1, $noreg, @g1, $noreg, killed renamable $eax, debug-location !24 :: (store (s32) into @g1, !tbaa !13)
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q2, $noreg, debug-location !25 :: (dereferenceable load (s32) from @q2, !tbaa !13)
+    CMP32ri8 renamable $eax, 6, implicit-def $eflags, debug-location !26
+    JCC_1 %bb.4, 12, implicit killed $eflags, debug-location !29
+    JMP_1 %bb.3, debug-location !29
+
+  bb.3.if.then.i3:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !30
+
+  bb.4.inl4.exit:
+    liveins: $eax
+
+    renamable $eax = nsw IMUL32rr killed renamable $eax, renamable $eax, implicit-def dead $eflags, debug-location !31
+    renamable $eax = nuw nsw ADD32ri killed renamable $eax, 200, implicit-def dead $eflags, debug-location !32
+    MOV32mr $rip, 1, $noreg, @g2, $noreg, killed renamable $eax, debug-location !33 :: (store (s32) into @g2, !tbaa !13)
+    $eax = MOV32r0 implicit-def dead $eflags, debug-location !34
+    $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34
+    frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !34
+    RET 0, $eax, debug-location !34
+
+...
+
+# In this case we get a single abort call originated from two separate
+# inlined functions:
+#
+#  1 | #include <stdlib.h>
+#  2 |
+#  3 | static inline int inl3(int q, int n) {
+#  4 |   if (q > 3)
+#  5 |     abort();
+#  6 |   return q * 152 + n;
+#  7 | }
+#  8 |
+#  9 | static inline int inl4(int q, int n) {
+# 10 |   if (q > 5)
+# 11 |     abort();
+# 12 |   return q * q + n;
+# 13 | }
+# 14 |
+# 15 | int q1 = 1, q2 = 6;
+# 16 | int g1, g2;
+# 17 |
+# 18 | int multiple_inl_funcs()
+# 19 | {
+# 20 |   g1 = inl3(q1, 100);
+# 21 |   g2 = inl4(q2, 200);
+# 22 |   return 0;
+# 23 | }
+#
+# We should produce a location at line 0 in the most common scope,
+# multiple_inl_funcs(), without any inline information.
+
+# CHECK: [[INLINER:![0-9]+]] = distinct !DISubprogram(name: "multiple_inl_funcs"
+
+# CHECK-NOT: CALL64pcrel32
+# CHECK: CALL64pcrel32 target-flags(x86-plt) @abort, {{.*}} debug-location !DILocation(line: 0, scope: [[INLINER]])
+# CHECK-NOT: CALL64pcrel32

diff  --git a/llvm/test/DebugInfo/MIR/X86/merge-inline-loc4.mir b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc4.mir
new file mode 100644
index 000000000000..7ebeda4c7e82
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/merge-inline-loc4.mir
@@ -0,0 +1,166 @@
+# RUN: llc -mtriple=x86_64-pc-linux %s -run-pass=branch-folder -o - | FileCheck %s
+
+--- |
+  ; ModuleID = 'case4.c'
+  source_filename = "case4.c"
+  target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+  target triple = "x86_64-unknown-linux-gnu"
+
+  @q1 = dso_local local_unnamed_addr global i32 1, align 4
+  @q2 = dso_local local_unnamed_addr global i32 6, align 4
+  @g1 = dso_local local_unnamed_addr global i32 0, align 4
+
+  ; Function Attrs: nounwind uwtable
+  define dso_local i32 @merge_inl_and_non_inl() local_unnamed_addr #0 !dbg !9 {
+  entry:
+    %0 = load i32, ptr @q1, align 4, !dbg !12, !tbaa !13
+    %cmp.i = icmp sgt i32 %0, 3, !dbg !17
+    br i1 %cmp.i, label %if.then.i, label %inl5.exit, !dbg !20
+
+  if.then.i:                                        ; preds = %entry
+    tail call void @abort() #2, !dbg !21
+    unreachable, !dbg !21
+
+  inl5.exit:                                        ; preds = %entry
+    %mul.i = mul nsw i32 %0, 152, !dbg !22
+    %add.i = add nsw i32 %mul.i, 100, !dbg !23
+    store i32 %add.i, ptr @g1, align 4, !dbg !24, !tbaa !13
+    %1 = load i32, ptr @q2, align 4, !dbg !25, !tbaa !13
+    %cmp = icmp sgt i32 %1, 5, !dbg !26
+    br i1 %cmp, label %if.then, label %if.end, !dbg !25
+
+  if.then:                                          ; preds = %inl5.exit
+    tail call void @abort() #2, !dbg !27
+    unreachable, !dbg !27
+
+  if.end:                                           ; preds = %inl5.exit
+    ret i32 0, !dbg !28
+  }
+
+  ; Function Attrs: noreturn nounwind
+  declare !dbg !29 void @abort() local_unnamed_addr #1
+
+  attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #1 = { noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+  attributes #2 = { noreturn 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 16.0.0.prerel", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
+  !1 = !DIFile(filename: "case4.c", directory: "/", checksumkind: CSK_MD5, checksum: "6ffe3d1878ac05f79e8c3a30566e1b2a")
+  !2 = !{i32 7, !"Dwarf Version", i32 5}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = !{i32 1, !"wchar_size", i32 4}
+  !5 = !{i32 8, !"PIC Level", i32 2}
+  !6 = !{i32 7, !"PIE Level", i32 2}
+  !7 = !{i32 7, !"uwtable", i32 2}
+  !8 = !{!"clang version 16.0.0.prerel"}
+  !9 = distinct !DISubprogram(name: "merge_inl_and_non_inl", scope: !1, file: !1, line: 12, type: !10, scopeLine: 13, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !10 = !DISubroutineType(types: !11)
+  !11 = !{}
+  !12 = !DILocation(line: 14, column: 13, scope: !9)
+  !13 = !{!14, !14, i64 0}
+  !14 = !{!"int", !15, i64 0}
+  !15 = !{!"omnipotent char", !16, i64 0}
+  !16 = !{!"Simple C/C++ TBAA"}
+  !17 = !DILocation(line: 4, column: 9, scope: !18, inlinedAt: !19)
+  !18 = distinct !DISubprogram(name: "inl5", scope: !1, file: !1, line: 3, type: !10, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+  !19 = distinct !DILocation(line: 14, column: 8, scope: !9)
+  !20 = !DILocation(line: 4, column: 7, scope: !18, inlinedAt: !19)
+  !21 = !DILocation(line: 5, column: 5, scope: !18, inlinedAt: !19)
+  !22 = !DILocation(line: 6, column: 12, scope: !18, inlinedAt: !19)
+  !23 = !DILocation(line: 6, column: 18, scope: !18, inlinedAt: !19)
+  !24 = !DILocation(line: 14, column: 6, scope: !9)
+  !25 = !DILocation(line: 15, column: 7, scope: !9)
+  !26 = !DILocation(line: 15, column: 10, scope: !9)
+  !27 = !DILocation(line: 16, column: 5, scope: !9)
+  !28 = !DILocation(line: 17, column: 3, scope: !9)
+  !29 = !DISubprogram(name: "abort", scope: !30, file: !30, line: 514, type: !10, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !11)
+  !30 = !DIFile(filename: "/usr/include/stdlib.h", directory: "", checksumkind: CSK_MD5, checksum: "f7a1412d75d9e3df251dfc21b02d59ef")
+
+...
+---
+name:            merge_inl_and_non_inl
+alignment:       16
+tracksRegLiveness: true
+tracksDebugUserValues: true
+frameInfo:
+  stackSize:       8
+  offsetAdjustment: -8
+  maxAlignment:    1
+  adjustsStack:    true
+  hasCalls:        true
+  maxCallFrameSize: 0
+machineFunctionInfo: {}
+body:             |
+  bb.0.entry:
+    successors: %bb.1(0x00000800), %bb.2(0x7ffff800)
+
+    frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
+    frame-setup CFI_INSTRUCTION def_cfa_offset 16
+    renamable $eax = MOV32rm $rip, 1, $noreg, @q1, $noreg, debug-location !12 :: (dereferenceable load (s32) from @q1, !tbaa !13)
+    CMP32ri8 renamable $eax, 4, implicit-def $eflags, debug-location !17
+    JCC_1 %bb.2, 12, implicit killed $eflags, debug-location !20
+    JMP_1 %bb.1, debug-location !20
+
+  bb.1.if.then.i:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !21
+
+  bb.2.inl5.exit:
+    successors: %bb.3(0x00000800), %bb.4(0x7ffff800)
+    liveins: $eax
+
+    renamable $eax = nsw IMUL32rri killed renamable $eax, 152, implicit-def dead $eflags, debug-location !22
+    renamable $eax = nsw ADD32ri8 killed renamable $eax, 100, implicit-def dead $eflags, debug-location !23
+    MOV32mr $rip, 1, $noreg, @g1, $noreg, killed renamable $eax, debug-location !24 :: (store (s32) into @g1, !tbaa !13)
+    CMP32mi8 $rip, 1, $noreg, @q2, $noreg, 6, implicit-def $eflags, debug-location !26 :: (dereferenceable load (s32) from @q2, !tbaa !13)
+    JCC_1 %bb.4, 12, implicit killed $eflags, debug-location !25
+    JMP_1 %bb.3, debug-location !25
+
+  bb.3.if.then:
+    successors:
+
+    CALL64pcrel32 target-flags(x86-plt) @abort, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !27
+
+  bb.4.if.end:
+    $eax = MOV32r0 implicit-def dead $eflags, debug-location !28
+    $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !28
+    frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !28
+    RET 0, $eax, debug-location !28
+
+...
+
+# In this case we get a single abort call originating from an inlined instance
+# of inl5() and the function that is inlined in:
+#
+#  1 | #include <stdlib.h>
+#  2 |
+#  3 | static inline int inl5(int q, int n) {
+#  4 |   if (q > 3)
+#  5 |     abort();
+#  6 |   return q * 152 + n;
+#  7 | }
+#  8 |
+#  9 | int q1 = 1, q2 = 6;
+# 10 | int g1;
+# 11 |
+# 12 | int merge_inl_and_non_inl()
+# 13 | {
+# 14 |   g1 = inl5(q1, 100);
+# 15 |   if (q2 > 5)
+# 16 |     abort();
+# 17 |   return 0;
+# 18 | }
+#
+# We should produce a location at line 0 in the most common scope,
+# merge_inl_and_non_inl(), without any inline information.
+
+# CHECK: [[INLINER:![0-9]+]] = distinct !DISubprogram(name: "merge_inl_and_non_inl"
+
+# CHECK-NOT: CALL64pcrel32
+# CHECK: CALL64pcrel32 target-flags(x86-plt) @abort, {{.*}} debug-location !DILocation(line: 0, scope: [[INLINER]])
+# CHECK-NOT: CALL64pcrel32

diff  --git a/llvm/test/DebugInfo/X86/merge_inlined_loc.ll b/llvm/test/DebugInfo/X86/merge_inlined_loc.ll
index 5961f843173a..07a72d6d30d7 100644
--- a/llvm/test/DebugInfo/X86/merge_inlined_loc.ll
+++ b/llvm/test/DebugInfo/X86/merge_inlined_loc.ll
@@ -6,8 +6,8 @@
 ; locations with the same source line but 
diff erent scopes should be merged to
 ; a line zero location at the nearest common scope and inlining. The location
 ; of the single call to "common" (the two calls are collapsed together by
-; BranchFolding) should be attributed to line zero inside the wrapper2 inlined
-; scope within f1.
+; BranchFolding) should be attributed to line 2 inside the wrapper inlined
+; scope within wrapper2 at line 0 inlined within f1 at line 13.
 
 ; void common();
 ; inline void wrapper() { common(); }
@@ -22,10 +22,12 @@
 ; }
 ; void f1() { wrapper2(); }
 
-; Ensure there is only one inlined_subroutine (for wrapper2, none for wrapper)
-; & that its address range includes the call to 'common'.
+; Ensure there are two inlined_subroutine (for wrapper and wrapper2).
 
-; CHECK: jmp _Z6commonv
+; CHECK: .loc 1 2 25 epilogue_begin
+; CHECK-NEXT: popq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 8
+; CHECK-NEXT: jmp _Z6commonv
 ; CHECK-NEXT: [[LABEL:.*]]:
 
 ; CHECK: .section .debug_info
@@ -35,6 +37,9 @@
 ; CHECK:   DW_TAG_inlined_subroutine
 ; CHECK-NOT: {{DW_TAG\|End Of Children}}
 ; CHECK:     [[LABEL]]-{{.*}} DW_AT_high_pc
+; CHECK:     .byte 13 # DW_AT_call_line
+; CHECK:   DW_TAG_inlined_subroutine
+; CHECK:     .byte 0 # DW_AT_call_line
 ; CHECK-NOT: DW_TAG
 
 

diff  --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 85d394a9fbb8..04d9443ef185 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -1089,6 +1089,159 @@ TEST_F(DILocationTest, Merge) {
     EXPECT_EQ(SPI, M->getScope());
     EXPECT_EQ(nullptr, M->getInlinedAt());
   }
+
+  // Merge a location in C, which is inlined-at in B that is inlined in A,
+  // with a location in A that has the same scope, line and column as B's
+  // inlined-at location.
+  {
+    auto *FA = getFile();
+    auto *FB = getFile();
+    auto *FC = 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 *SPC = DISubprogram::getDistinct(Context, FC, "c", "c", FC, 0, nullptr,
+                                          0, nullptr, 0, 0, DINode::FlagZero,
+                                          DISubprogram::SPFlagZero, nullptr);
+
+    auto *A = DILocation::get(Context, 3, 2, SPA);
+    auto *B = DILocation::get(Context, 2, 4, SPB, A);
+    auto *C = DILocation::get(Context, 13, 2, SPC, B);
+    auto *M = DILocation::getMergedLocation(A, C);
+    EXPECT_EQ(3u, M->getLine());
+    EXPECT_EQ(2u, M->getColumn());
+    EXPECT_TRUE(isa<DILocalScope>(M->getScope()));
+    EXPECT_EQ(SPA, M->getScope());
+    EXPECT_EQ(nullptr, M->getInlinedAt());
+  }
+
+  // Two inlined locations with the same scope, line and column
+  // in the same inlined-at function at 
diff erent line and column.
+  {
+    auto *FA = getFile();
+    auto *FB = getFile();
+    auto *FC = 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 *SPC = DISubprogram::getDistinct(Context, FC, "c", "c", FC, 0, nullptr,
+                                          0, nullptr, 0, 0, DINode::FlagZero,
+                                          DISubprogram::SPFlagZero, nullptr);
+
+    auto *A = DILocation::get(Context, 10, 20, SPA);
+    auto *B1 = DILocation::get(Context, 3, 2, SPB, A);
+    auto *B2 = DILocation::get(Context, 4, 5, SPB, A);
+    auto *C1 = DILocation::get(Context, 2, 4, SPC, B1);
+    auto *C2 = DILocation::get(Context, 2, 4, SPC, B2);
+
+    auto *M = DILocation::getMergedLocation(C1, C2);
+    EXPECT_EQ(2u, M->getLine());
+    EXPECT_EQ(4u, M->getColumn());
+    EXPECT_EQ(SPC, M->getScope());
+    ASSERT_NE(nullptr, M->getInlinedAt());
+
+    auto *I1 = M->getInlinedAt();
+    EXPECT_EQ(0u, I1->getLine());
+    EXPECT_EQ(0u, I1->getColumn());
+    EXPECT_EQ(SPB, I1->getScope());
+    EXPECT_EQ(A, I1->getInlinedAt());
+  }
+
+  // Two locations, 
diff erent line/column and scope in the same subprogram,
+  // inlined at the same place. This should result in a 0:0 location with
+  // the nearest common scope in the inlined function.
+  {
+    auto *FA = 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 *SPI = DISubprogram::getDistinct(Context, FI, "i", "i", FI, 0, nullptr,
+                                          0, nullptr, 0, 0, DINode::FlagZero,
+                                          DISubprogram::SPFlagZero, nullptr);
+
+    // Nearest common scope for the two locations in a.
+    auto *SPAScope1 = DILexicalBlock::getDistinct(Context, SPA, FA, 4, 9);
+
+    // Scope for the first location in a.
+    auto *SPAScope2 =
+        DILexicalBlock::getDistinct(Context, SPAScope1, FA, 10, 12);
+
+    // Scope for the second location in a.
+    auto *SPAScope3 =
+        DILexicalBlock::getDistinct(Context, SPAScope1, FA, 20, 8);
+    auto *SPAScope4 =
+        DILexicalBlock::getDistinct(Context, SPAScope3, FA, 21, 12);
+
+    auto *I = DILocation::get(Context, 3, 8, SPI);
+    auto *A1 = DILocation::get(Context, 12, 7, SPAScope2, I);
+    auto *A2 = DILocation::get(Context, 21, 15, SPAScope4, I);
+    auto *M = DILocation::getMergedLocation(A1, A2);
+    EXPECT_EQ(0u, M->getLine());
+    EXPECT_EQ(0u, M->getColumn());
+    EXPECT_TRUE(isa<DILocalScope>(M->getScope()));
+    EXPECT_EQ(SPAScope1, M->getScope());
+    EXPECT_EQ(I, M->getInlinedAt());
+  }
+
+  // Regression test to catch a case where an iterator was invalidated due to
+  // handling the chain of inlined-at locations after the nearest common
+  // location for the two arguments were found.
+  {
+    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 *SPAScope1 = DILexicalBlock::getDistinct(Context, SPA, FA, 4, 9);
+    auto *SPAScope2 = DILexicalBlock::getDistinct(Context, SPA, FA, 8, 3);
+
+    DILocation *InlinedAt = nullptr;
+
+    // Create a chain of inlined-at locations.
+    for (int i = 0; i < 256; i++) {
+      InlinedAt = DILocation::get(Context, 3 + i, 8 + i, SPI, InlinedAt);
+    }
+
+    auto *A1 = DILocation::get(Context, 5, 9, SPAScope1, InlinedAt);
+    auto *A2 = DILocation::get(Context, 9, 8, SPAScope2, InlinedAt);
+    auto *B = DILocation::get(Context, 10, 3, SPB, A1);
+    auto *M1 = DILocation::getMergedLocation(B, A2);
+    EXPECT_EQ(0u, M1->getLine());
+    EXPECT_EQ(0u, M1->getColumn());
+    EXPECT_TRUE(isa<DILocalScope>(M1->getScope()));
+    EXPECT_EQ(SPA, M1->getScope());
+    EXPECT_EQ(InlinedAt, M1->getInlinedAt());
+
+    // Test the other argument order for good measure.
+    auto *M2 = DILocation::getMergedLocation(A2, B);
+    EXPECT_EQ(M1, M2);
+  }
 }
 
 TEST_F(DILocationTest, getDistinct) {


        


More information about the llvm-commits mailing list