[llvm] 16cbb06 - [llvm-dwarfdump][LineCov] Add --variable-coverage-maybe-undefined option (#220855)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 03:54:17 PDT 2026


Author: unexpectedlydefined
Date: 2026-09-21T11:54:11+01:00
New Revision: 16cbb06c5ce1f1632b6d02346147caa17093e788

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

LOG: [llvm-dwarfdump][LineCov] Add --variable-coverage-maybe-undefined option (#220855)

This patch adds a `--variable-coverage-maybe-undefined` flag to
llvm-dwarfdump as part of the recently implemented feature that measures
DWARF coverage of local variables in terms of source lines. This causes
the IR-level analysis to consider variables live if they are defined on
any path, and therefore include lines where the variable may be
undefined. By default, variables must be defined on all paths to be
counted.

Additionally, it includes fixes for a few issues found by @SLTozer and
myself after merging the previous patches:
- Variables not present in the DWARF information that appear in the
baseline are now included in the output.
- The assertion that there are no `undef`/`poison` variable locations in
the IR has been removed, as in some circumstances it is possible to have
a `#dbg_declare(poison)` at `-O0`.
- A small error where entries were inserted into the file name map while
iterating through it has been fixed.

Added: 
    

Modified: 
    llvm/docs/CommandGuide/llvm-dwarfdump.md
    llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage-opt.ll
    llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage.ll
    llvm/test/tools/llvm-dwarfdump/X86/coverage.test
    llvm/tools/llvm-dwarfdump/Coverage.cpp
    llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
    llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-dwarfdump.md b/llvm/docs/CommandGuide/llvm-dwarfdump.md
index ff4454241bd0c..ef125336549ce 100644
--- a/llvm/docs/CommandGuide/llvm-dwarfdump.md
+++ b/llvm/docs/CommandGuide/llvm-dwarfdump.md
@@ -206,6 +206,13 @@ variable definedness in coverage statistics (implies
 {option}`--show-variable-coverage`)
 :::
 
+:::{option} --variable-coverage-maybe-undefined
+Use with {option}`--show-variable-coverage` and
+{option}`--variable-coverage-bitcode-file` to consider variables
+live if they are defined on at least one path (default behaviour is
+to require a variable to be defined on all paths to be counted)
+:::
+
 :::{option} --combine-inline-variable-instances
 Use with {option}`--show-variable-coverage` to average variable
 coverage across inlined subroutine instances instead of printing

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage-opt.ll b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage-opt.ll
index e765e49db1d18..f8bb701856afb 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage-opt.ll
+++ b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage-opt.ll
@@ -17,11 +17,27 @@
 ;   fn2(u);
 ; }
 ; 
+; __attribute__((noinline)) void fn0(int x, int y) {
+;   int u = x + y;
+;   int a;
+;   if (y > 4) {
+;     u += x;
+;     a = 7;
+;     fn2(a);
+;   } else if (y > 5) {
+;     int v = u;
+;     v++;
+;   }
+;   u--;
+;   fn2(u);
+; }
+; 
 ; __attribute__((noinline)) int f() {
 ;   int l, k;
 ;   fn3(&l);
 ;   fn3(&k);
 ;   fn1(l, k);
+;   fn0(l, k);
 ;   return 0;
 ; }
 
@@ -36,57 +52,85 @@ define dso_local void @fn1(i32 noundef %0, i32 noundef %1) local_unnamed_addr !d
     #dbg_value(i32 %1, !16, !DIExpression(), !20)
     #dbg_value(i32 poison, !17, !DIExpression(), !20)
   %3 = icmp sgt i32 %0, 1, !dbg !21
-  %4 = select i1 %3, i32 1, i32 2, !dbg !23
+  %4 = select i1 %3, i32 1, i32 2, !dbg !21
     #dbg_value(i32 poison, !17, !DIExpression(), !20)
-  %5 = icmp sgt i32 %1, 4, !dbg !24
-  %6 = select i1 %5, i32 %0, i32 0, !dbg !26
+  %5 = icmp sgt i32 %1, 4, !dbg !23
+  %6 = select i1 %5, i32 %0, i32 0, !dbg !23
     #dbg_value(i32 poison, !17, !DIExpression(), !20)
     #dbg_value(i32 7, !18, !DIExpression(), !20)
-  tail call void @fn2(i32 noundef 7), !dbg !27
+  tail call void @fn2(i32 noundef 7), !dbg !25
     #dbg_value(i32 poison, !19, !DIExpression(), !20)
     #dbg_value(i32 poison, !19, !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value), !20)
-  %7 = add i32 %0, -1, !dbg !28
-  %8 = add i32 %7, %1, !dbg !23
-  %9 = add i32 %8, %4, !dbg !26
-  %10 = add i32 %9, %6, !dbg !29
+  %7 = add i32 %0, -1, !dbg !26
+  %8 = add i32 %7, %1, !dbg !21
+  %9 = add i32 %8, %4, !dbg !23
+  %10 = add i32 %9, %6, !dbg !27
     #dbg_value(i32 %10, !17, !DIExpression(), !20)
-  tail call void @fn2(i32 noundef %10), !dbg !30
-  ret void, !dbg !31
+  tail call void @fn2(i32 noundef %10), !dbg !28
+  ret void, !dbg !29
 }
 
 ; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
-declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none))
 
-declare !dbg !32 void @fn2(i32 noundef) local_unnamed_addr
+declare !dbg !30 void @fn2(i32 noundef) local_unnamed_addr
 
 ; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
-declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr captures(none))
+
+; Function Attrs: noinline nounwind uwtable
+define dso_local void @fn0(i32 noundef %0, i32 noundef %1) local_unnamed_addr !dbg !33 {
+    #dbg_value(i32 %0, !35, !DIExpression(), !43)
+    #dbg_value(i32 %1, !36, !DIExpression(), !43)
+  %3 = add nsw i32 %1, %0, !dbg !44
+    #dbg_value(i32 %3, !37, !DIExpression(), !43)
+  %4 = icmp sgt i32 %1, 4, !dbg !45
+  br i1 %4, label %5, label %7, !dbg !45
+
+5:                                                ; preds = %2
+  %6 = add nsw i32 %3, %0, !dbg !46
+    #dbg_value(i32 %6, !37, !DIExpression(), !43)
+    #dbg_value(i32 7, !38, !DIExpression(), !43)
+  tail call void @fn2(i32 noundef 7), !dbg !48
+  br label %7, !dbg !49
+
+7:                                                ; preds = %2, %5
+  %8 = phi i32 [ %6, %5 ], [ %3, %2 ], !dbg !43
+    #dbg_value(i32 %8, !37, !DIExpression(), !43)
+  %9 = add nsw i32 %8, -1, !dbg !50
+    #dbg_value(i32 %9, !37, !DIExpression(), !43)
+  tail call void @fn2(i32 noundef %9), !dbg !51
+  ret void, !dbg !52
+}
 
 ; Function Attrs: noinline nounwind uwtable
-define dso_local noundef i32 @f() local_unnamed_addr !dbg !35 {
-  %1 = alloca i32, align 4, !DIAssignID !41
-    #dbg_assign(i1 poison, !39, !DIExpression(), !41, ptr %1, !DIExpression(), !42)
-  %2 = alloca i32, align 4, !DIAssignID !43
-    #dbg_assign(i1 poison, !40, !DIExpression(), !43, ptr %2, !DIExpression(), !42)
-  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %1), !dbg !44
-  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %2), !dbg !44
-  call void @fn3(ptr noundef nonnull %1), !dbg !45
-  call void @fn3(ptr noundef nonnull %2), !dbg !46
-  %3 = load i32, ptr %1, align 4, !dbg !47, !tbaa !48
-  %4 = load i32, ptr %2, align 4, !dbg !52, !tbaa !48
-  call void @fn1(i32 noundef %3, i32 noundef %4), !dbg !53
-  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %2), !dbg !54
-  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %1), !dbg !54
-  ret i32 0, !dbg !55
+define dso_local noundef i32 @f() local_unnamed_addr !dbg !53 {
+  %1 = alloca i32, align 4, !DIAssignID !59
+    #dbg_assign(i1 poison, !57, !DIExpression(), !59, ptr %1, !DIExpression(), !60)
+  %2 = alloca i32, align 4, !DIAssignID !61
+    #dbg_assign(i1 poison, !58, !DIExpression(), !61, ptr %2, !DIExpression(), !60)
+  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %1), !dbg !62
+  call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %2), !dbg !62
+  call void @fn3(ptr noundef nonnull %1), !dbg !63
+  call void @fn3(ptr noundef nonnull %2), !dbg !64
+  %3 = load i32, ptr %1, align 4, !dbg !65, !tbaa !66
+  %4 = load i32, ptr %2, align 4, !dbg !70, !tbaa !66
+  call void @fn1(i32 noundef %3, i32 noundef %4), !dbg !71
+  %5 = load i32, ptr %1, align 4, !dbg !72, !tbaa !66
+  %6 = load i32, ptr %2, align 4, !dbg !73, !tbaa !66
+  call void @fn0(i32 noundef %5, i32 noundef %6), !dbg !74
+  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %2), !dbg !75
+  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %1), !dbg !75
+  ret i32 0, !dbg !76
 }
 
-declare !dbg !56 void @fn3(ptr noundef) local_unnamed_addr
+declare !dbg !77 void @fn3(ptr noundef) local_unnamed_addr
 
 !llvm.dbg.cu = !{!0}
 !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
 !llvm.ident = !{!9}
 
-!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 19.1.7", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.1.8", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
 !1 = !DIFile(filename: "test.c", directory: "/")
 !2 = !{i32 7, !"Dwarf Version", i32 5}
 !3 = !{i32 2, !"Debug Info Version", i32 3}
@@ -95,7 +139,7 @@ declare !dbg !56 void @fn3(ptr noundef) local_unnamed_addr
 !6 = !{i32 7, !"PIE Level", i32 2}
 !7 = !{i32 7, !"uwtable", i32 2}
 !8 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
-!9 = !{!"clang version 19.1.7"}
+!9 = !{!"clang version 21.1.8"}
 !10 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 3, type: !11, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
 !11 = !DISubroutineType(types: !12)
 !12 = !{null, !13, !13}
@@ -109,40 +153,61 @@ declare !dbg !56 void @fn3(ptr noundef) local_unnamed_addr
 !20 = !DILocation(line: 0, scope: !10)
 !21 = !DILocation(line: 5, column: 9, scope: !22)
 !22 = distinct !DILexicalBlock(scope: !10, file: !1, line: 5, column: 7)
-!23 = !DILocation(line: 5, column: 7, scope: !10)
-!24 = !DILocation(line: 9, column: 9, scope: !25)
-!25 = distinct !DILexicalBlock(scope: !10, file: !1, line: 9, column: 7)
-!26 = !DILocation(line: 9, column: 7, scope: !10)
-!27 = !DILocation(line: 12, column: 3, scope: !10)
-!28 = !DILocation(line: 4, column: 13, scope: !10)
-!29 = !DILocation(line: 15, column: 4, scope: !10)
-!30 = !DILocation(line: 16, column: 3, scope: !10)
-!31 = !DILocation(line: 17, column: 1, scope: !10)
-!32 = !DISubprogram(name: "fn2", scope: !1, file: !1, line: 2, type: !33, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
-!33 = !DISubroutineType(types: !34)
-!34 = !{null, !13}
-!35 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 19, type: !36, scopeLine: 19, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !38)
-!36 = !DISubroutineType(types: !37)
-!37 = !{!13}
-!38 = !{!39, !40}
-!39 = !DILocalVariable(name: "l", scope: !35, file: !1, line: 20, type: !13)
-!40 = !DILocalVariable(name: "k", scope: !35, file: !1, line: 20, type: !13)
-!41 = distinct !DIAssignID()
-!42 = !DILocation(line: 0, scope: !35)
-!43 = distinct !DIAssignID()
-!44 = !DILocation(line: 20, column: 3, scope: !35)
-!45 = !DILocation(line: 21, column: 3, scope: !35)
-!46 = !DILocation(line: 22, column: 3, scope: !35)
-!47 = !DILocation(line: 23, column: 7, scope: !35)
-!48 = !{!49, !49, i64 0}
-!49 = !{!"int", !50, i64 0}
-!50 = !{!"omnipotent char", !51, i64 0}
-!51 = !{!"Simple C/C++ TBAA"}
-!52 = !DILocation(line: 23, column: 10, scope: !35)
-!53 = !DILocation(line: 23, column: 3, scope: !35)
-!54 = !DILocation(line: 25, column: 1, scope: !35)
-!55 = !DILocation(line: 24, column: 3, scope: !35)
-!56 = !DISubprogram(name: "fn3", scope: !1, file: !1, line: 1, type: !57, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
-!57 = !DISubroutineType(types: !58)
-!58 = !{null, !59}
-!59 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)
+!23 = !DILocation(line: 9, column: 9, scope: !24)
+!24 = distinct !DILexicalBlock(scope: !10, file: !1, line: 9, column: 7)
+!25 = !DILocation(line: 12, column: 3, scope: !10)
+!26 = !DILocation(line: 4, column: 13, scope: !10)
+!27 = !DILocation(line: 15, column: 4, scope: !10)
+!28 = !DILocation(line: 16, column: 3, scope: !10)
+!29 = !DILocation(line: 17, column: 1, scope: !10)
+!30 = !DISubprogram(name: "fn2", scope: !1, file: !1, line: 2, type: !31, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
+!31 = !DISubroutineType(types: !32)
+!32 = !{null, !13}
+!33 = distinct !DISubprogram(name: "fn0", scope: !1, file: !1, line: 19, type: !11, scopeLine: 19, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !34)
+!34 = !{!35, !36, !37, !38, !39}
+!35 = !DILocalVariable(name: "x", arg: 1, scope: !33, file: !1, line: 19, type: !13)
+!36 = !DILocalVariable(name: "y", arg: 2, scope: !33, file: !1, line: 19, type: !13)
+!37 = !DILocalVariable(name: "u", scope: !33, file: !1, line: 20, type: !13)
+!38 = !DILocalVariable(name: "a", scope: !33, file: !1, line: 21, type: !13)
+!39 = !DILocalVariable(name: "v", scope: !40, file: !1, line: 27, type: !13)
+!40 = distinct !DILexicalBlock(scope: !41, file: !1, line: 26, column: 21)
+!41 = distinct !DILexicalBlock(scope: !42, file: !1, line: 26, column: 14)
+!42 = distinct !DILexicalBlock(scope: !33, file: !1, line: 22, column: 7)
+!43 = !DILocation(line: 0, scope: !33)
+!44 = !DILocation(line: 20, column: 13, scope: !33)
+!45 = !DILocation(line: 22, column: 9, scope: !42)
+!46 = !DILocation(line: 23, column: 7, scope: !47)
+!47 = distinct !DILexicalBlock(scope: !42, file: !1, line: 22, column: 14)
+!48 = !DILocation(line: 25, column: 5, scope: !47)
+!49 = !DILocation(line: 26, column: 3, scope: !47)
+!50 = !DILocation(line: 30, column: 4, scope: !33)
+!51 = !DILocation(line: 31, column: 3, scope: !33)
+!52 = !DILocation(line: 32, column: 1, scope: !33)
+!53 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 34, type: !54, scopeLine: 34, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !56)
+!54 = !DISubroutineType(types: !55)
+!55 = !{!13}
+!56 = !{!57, !58}
+!57 = !DILocalVariable(name: "l", scope: !53, file: !1, line: 35, type: !13)
+!58 = !DILocalVariable(name: "k", scope: !53, file: !1, line: 35, type: !13)
+!59 = distinct !DIAssignID()
+!60 = !DILocation(line: 0, scope: !53)
+!61 = distinct !DIAssignID()
+!62 = !DILocation(line: 35, column: 3, scope: !53)
+!63 = !DILocation(line: 36, column: 3, scope: !53)
+!64 = !DILocation(line: 37, column: 3, scope: !53)
+!65 = !DILocation(line: 38, column: 7, scope: !53)
+!66 = !{!67, !67, i64 0}
+!67 = !{!"int", !68, i64 0}
+!68 = !{!"omnipotent char", !69, i64 0}
+!69 = !{!"Simple C/C++ TBAA"}
+!70 = !DILocation(line: 38, column: 10, scope: !53)
+!71 = !DILocation(line: 38, column: 3, scope: !53)
+!72 = !DILocation(line: 39, column: 7, scope: !53)
+!73 = !DILocation(line: 39, column: 10, scope: !53)
+!74 = !DILocation(line: 39, column: 3, scope: !53)
+!75 = !DILocation(line: 41, column: 1, scope: !53)
+!76 = !DILocation(line: 40, column: 3, scope: !53)
+!77 = !DISubprogram(name: "fn3", scope: !1, file: !1, line: 1, type: !78, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
+!78 = !DISubroutineType(types: !79)
+!79 = !{null, !80}
+!80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage.ll b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage.ll
index aa7dd2f89c855..e0f843d635952 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage.ll
+++ b/llvm/test/tools/llvm-dwarfdump/X86/Inputs/coverage.ll
@@ -17,11 +17,27 @@
 ;   fn2(u);
 ; }
 ; 
+; __attribute__((noinline)) void fn0(int x, int y) {
+;   int u = x + y;
+;   int a;
+;   if (y > 4) {
+;     u += x;
+;     a = 7;
+;     fn2(a);
+;   } else if (y > 5) {
+;     int v = u;
+;     v++;
+;   }
+;   u--;
+;   fn2(u);
+; }
+; 
 ; __attribute__((noinline)) int f() {
 ;   int l, k;
 ;   fn3(&l);
 ;   fn3(&k);
 ;   fn1(l, k);
+;   fn0(l, k);
 ;   return 0;
 ; }
 
@@ -48,65 +64,125 @@ define dso_local void @fn1(i32 noundef %0, i32 noundef %1) !dbg !10 {
   store i32 %10, ptr %5, align 4, !dbg !20
   %11 = load i32, ptr %3, align 4, !dbg !24
   %12 = icmp sgt i32 %11, 1, !dbg !26
-  br i1 %12, label %13, label %16, !dbg !27
+  br i1 %12, label %13, label %16, !dbg !26
 
 13:                                               ; preds = %2
-  %14 = load i32, ptr %5, align 4, !dbg !28
-  %15 = add nsw i32 %14, 1, !dbg !28
-  store i32 %15, ptr %5, align 4, !dbg !28
-  br label %19, !dbg !29
+  %14 = load i32, ptr %5, align 4, !dbg !27
+  %15 = add nsw i32 %14, 1, !dbg !27
+  store i32 %15, ptr %5, align 4, !dbg !27
+  br label %19, !dbg !28
 
 16:                                               ; preds = %2
-  %17 = load i32, ptr %5, align 4, !dbg !30
-  %18 = add nsw i32 %17, 2, !dbg !30
-  store i32 %18, ptr %5, align 4, !dbg !30
+  %17 = load i32, ptr %5, align 4, !dbg !29
+  %18 = add nsw i32 %17, 2, !dbg !29
+  store i32 %18, ptr %5, align 4, !dbg !29
   br label %19
 
 19:                                               ; preds = %16, %13
-  %20 = load i32, ptr %4, align 4, !dbg !31
-  %21 = icmp sgt i32 %20, 4, !dbg !33
-  br i1 %21, label %22, label %26, !dbg !34
+  %20 = load i32, ptr %4, align 4, !dbg !30
+  %21 = icmp sgt i32 %20, 4, !dbg !32
+  br i1 %21, label %22, label %26, !dbg !32
 
 22:                                               ; preds = %19
-  %23 = load i32, ptr %3, align 4, !dbg !35
-  %24 = load i32, ptr %5, align 4, !dbg !36
-  %25 = add nsw i32 %24, %23, !dbg !36
-  store i32 %25, ptr %5, align 4, !dbg !36
-  br label %26, !dbg !37
+  %23 = load i32, ptr %3, align 4, !dbg !33
+  %24 = load i32, ptr %5, align 4, !dbg !34
+  %25 = add nsw i32 %24, %23, !dbg !34
+  store i32 %25, ptr %5, align 4, !dbg !34
+  br label %26, !dbg !35
 
 26:                                               ; preds = %22, %19
-    #dbg_declare(ptr %6, !38, !DIExpression(), !39)
-  store i32 7, ptr %6, align 4, !dbg !39
-  %27 = load i32, ptr %6, align 4, !dbg !40
-  call void @fn2(i32 noundef %27), !dbg !41
-    #dbg_declare(ptr %7, !42, !DIExpression(), !43)
-  %28 = load i32, ptr %5, align 4, !dbg !44
-  store i32 %28, ptr %7, align 4, !dbg !43
-  %29 = load i32, ptr %7, align 4, !dbg !45
-  %30 = add nsw i32 %29, 1, !dbg !45
-  store i32 %30, ptr %7, align 4, !dbg !45
-  %31 = load i32, ptr %5, align 4, !dbg !46
-  %32 = add nsw i32 %31, -1, !dbg !46
-  store i32 %32, ptr %5, align 4, !dbg !46
-  %33 = load i32, ptr %5, align 4, !dbg !47
-  call void @fn2(i32 noundef %33), !dbg !48
-  ret void, !dbg !49
+    #dbg_declare(ptr %6, !36, !DIExpression(), !37)
+  store i32 7, ptr %6, align 4, !dbg !37
+  %27 = load i32, ptr %6, align 4, !dbg !38
+  call void @fn2(i32 noundef %27), !dbg !39
+    #dbg_declare(ptr %7, !40, !DIExpression(), !41)
+  %28 = load i32, ptr %5, align 4, !dbg !42
+  store i32 %28, ptr %7, align 4, !dbg !41
+  %29 = load i32, ptr %7, align 4, !dbg !43
+  %30 = add nsw i32 %29, 1, !dbg !43
+  store i32 %30, ptr %7, align 4, !dbg !43
+  %31 = load i32, ptr %5, align 4, !dbg !44
+  %32 = add nsw i32 %31, -1, !dbg !44
+  store i32 %32, ptr %5, align 4, !dbg !44
+  %33 = load i32, ptr %5, align 4, !dbg !45
+  call void @fn2(i32 noundef %33), !dbg !46
+  ret void, !dbg !47
 }
 
 declare void @fn2(i32 noundef)
 
 ; Function Attrs: noinline nounwind optnone uwtable
-define dso_local i32 @f() !dbg !50 {
+define dso_local void @fn0(i32 noundef %0, i32 noundef %1) !dbg !48 {
+  %3 = alloca i32, align 4
+  %4 = alloca i32, align 4
+  %5 = alloca i32, align 4
+  %6 = alloca i32, align 4
+  %7 = alloca i32, align 4
+  store i32 %0, ptr %3, align 4
+    #dbg_declare(ptr %3, !49, !DIExpression(), !50)
+  store i32 %1, ptr %4, align 4
+    #dbg_declare(ptr %4, !51, !DIExpression(), !52)
+    #dbg_declare(ptr %5, !53, !DIExpression(), !54)
+  %8 = load i32, ptr %3, align 4, !dbg !55
+  %9 = load i32, ptr %4, align 4, !dbg !56
+  %10 = add nsw i32 %8, %9, !dbg !57
+  store i32 %10, ptr %5, align 4, !dbg !54
+    #dbg_declare(ptr %6, !58, !DIExpression(), !59)
+  %11 = load i32, ptr %4, align 4, !dbg !60
+  %12 = icmp sgt i32 %11, 4, !dbg !62
+  br i1 %12, label %13, label %18, !dbg !62
+
+13:                                               ; preds = %2
+  %14 = load i32, ptr %3, align 4, !dbg !63
+  %15 = load i32, ptr %5, align 4, !dbg !65
+  %16 = add nsw i32 %15, %14, !dbg !65
+  store i32 %16, ptr %5, align 4, !dbg !65
+  store i32 7, ptr %6, align 4, !dbg !66
+  %17 = load i32, ptr %6, align 4, !dbg !67
+  call void @fn2(i32 noundef %17), !dbg !68
+  br label %26, !dbg !69
+
+18:                                               ; preds = %2
+  %19 = load i32, ptr %4, align 4, !dbg !70
+  %20 = icmp sgt i32 %19, 5, !dbg !72
+  br i1 %20, label %21, label %25, !dbg !72
+
+21:                                               ; preds = %18
+    #dbg_declare(ptr %7, !73, !DIExpression(), !75)
+  %22 = load i32, ptr %5, align 4, !dbg !76
+  store i32 %22, ptr %7, align 4, !dbg !75
+  %23 = load i32, ptr %7, align 4, !dbg !77
+  %24 = add nsw i32 %23, 1, !dbg !77
+  store i32 %24, ptr %7, align 4, !dbg !77
+  br label %25, !dbg !78
+
+25:                                               ; preds = %21, %18
+  br label %26
+
+26:                                               ; preds = %25, %13
+  %27 = load i32, ptr %5, align 4, !dbg !79
+  %28 = add nsw i32 %27, -1, !dbg !79
+  store i32 %28, ptr %5, align 4, !dbg !79
+  %29 = load i32, ptr %5, align 4, !dbg !80
+  call void @fn2(i32 noundef %29), !dbg !81
+  ret void, !dbg !82
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local i32 @f() !dbg !83 {
   %1 = alloca i32, align 4
   %2 = alloca i32, align 4
-    #dbg_declare(ptr %1, !53, !DIExpression(), !54)
-    #dbg_declare(ptr %2, !55, !DIExpression(), !56)
-  call void @fn3(ptr noundef %1), !dbg !57
-  call void @fn3(ptr noundef %2), !dbg !58
-  %3 = load i32, ptr %1, align 4, !dbg !59
-  %4 = load i32, ptr %2, align 4, !dbg !60
-  call void @fn1(i32 noundef %3, i32 noundef %4), !dbg !61
-  ret i32 0, !dbg !62
+    #dbg_declare(ptr %1, !86, !DIExpression(), !87)
+    #dbg_declare(ptr %2, !88, !DIExpression(), !89)
+  call void @fn3(ptr noundef %1), !dbg !90
+  call void @fn3(ptr noundef %2), !dbg !91
+  %3 = load i32, ptr %1, align 4, !dbg !92
+  %4 = load i32, ptr %2, align 4, !dbg !93
+  call void @fn1(i32 noundef %3, i32 noundef %4), !dbg !94
+  %5 = load i32, ptr %1, align 4, !dbg !95
+  %6 = load i32, ptr %2, align 4, !dbg !96
+  call void @fn0(i32 noundef %5, i32 noundef %6), !dbg !97
+  ret i32 0, !dbg !98
 }
 
 declare void @fn3(ptr noundef)
@@ -115,7 +191,7 @@ declare void @fn3(ptr noundef)
 !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
 !llvm.ident = !{!9}
 
-!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 19.1.7", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.1.8", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
 !1 = !DIFile(filename: "test.c", directory: "/")
 !2 = !{i32 7, !"Dwarf Version", i32 5}
 !3 = !{i32 2, !"Debug Info Version", i32 3}
@@ -124,7 +200,7 @@ declare void @fn3(ptr noundef)
 !6 = !{i32 7, !"PIE Level", i32 2}
 !7 = !{i32 7, !"uwtable", i32 2}
 !8 = !{i32 7, !"frame-pointer", i32 2}
-!9 = !{!"clang version 19.1.7"}
+!9 = !{!"clang version 21.1.8"}
 !10 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 3, type: !11, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
 !11 = !DISubroutineType(types: !12)
 !12 = !{null, !13, !13}
@@ -142,39 +218,75 @@ declare void @fn3(ptr noundef)
 !24 = !DILocation(line: 5, column: 7, scope: !25)
 !25 = distinct !DILexicalBlock(scope: !10, file: !1, line: 5, column: 7)
 !26 = !DILocation(line: 5, column: 9, scope: !25)
-!27 = !DILocation(line: 5, column: 7, scope: !10)
-!28 = !DILocation(line: 6, column: 7, scope: !25)
-!29 = !DILocation(line: 6, column: 5, scope: !25)
-!30 = !DILocation(line: 8, column: 7, scope: !25)
-!31 = !DILocation(line: 9, column: 7, scope: !32)
-!32 = distinct !DILexicalBlock(scope: !10, file: !1, line: 9, column: 7)
-!33 = !DILocation(line: 9, column: 9, scope: !32)
-!34 = !DILocation(line: 9, column: 7, scope: !10)
-!35 = !DILocation(line: 10, column: 10, scope: !32)
-!36 = !DILocation(line: 10, column: 7, scope: !32)
-!37 = !DILocation(line: 10, column: 5, scope: !32)
-!38 = !DILocalVariable(name: "a", scope: !10, file: !1, line: 11, type: !13)
-!39 = !DILocation(line: 11, column: 7, scope: !10)
-!40 = !DILocation(line: 12, column: 7, scope: !10)
-!41 = !DILocation(line: 12, column: 3, scope: !10)
-!42 = !DILocalVariable(name: "v", scope: !10, file: !1, line: 13, type: !13)
-!43 = !DILocation(line: 13, column: 7, scope: !10)
-!44 = !DILocation(line: 13, column: 11, scope: !10)
-!45 = !DILocation(line: 14, column: 4, scope: !10)
-!46 = !DILocation(line: 15, column: 4, scope: !10)
-!47 = !DILocation(line: 16, column: 7, scope: !10)
-!48 = !DILocation(line: 16, column: 3, scope: !10)
-!49 = !DILocation(line: 17, column: 1, scope: !10)
-!50 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 19, type: !51, scopeLine: 19, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
-!51 = !DISubroutineType(types: !52)
-!52 = !{!13}
-!53 = !DILocalVariable(name: "l", scope: !50, file: !1, line: 20, type: !13)
-!54 = !DILocation(line: 20, column: 7, scope: !50)
-!55 = !DILocalVariable(name: "k", scope: !50, file: !1, line: 20, type: !13)
-!56 = !DILocation(line: 20, column: 10, scope: !50)
-!57 = !DILocation(line: 21, column: 3, scope: !50)
-!58 = !DILocation(line: 22, column: 3, scope: !50)
-!59 = !DILocation(line: 23, column: 7, scope: !50)
-!60 = !DILocation(line: 23, column: 10, scope: !50)
-!61 = !DILocation(line: 23, column: 3, scope: !50)
-!62 = !DILocation(line: 24, column: 3, scope: !50)
+!27 = !DILocation(line: 6, column: 7, scope: !25)
+!28 = !DILocation(line: 6, column: 5, scope: !25)
+!29 = !DILocation(line: 8, column: 7, scope: !25)
+!30 = !DILocation(line: 9, column: 7, scope: !31)
+!31 = distinct !DILexicalBlock(scope: !10, file: !1, line: 9, column: 7)
+!32 = !DILocation(line: 9, column: 9, scope: !31)
+!33 = !DILocation(line: 10, column: 10, scope: !31)
+!34 = !DILocation(line: 10, column: 7, scope: !31)
+!35 = !DILocation(line: 10, column: 5, scope: !31)
+!36 = !DILocalVariable(name: "a", scope: !10, file: !1, line: 11, type: !13)
+!37 = !DILocation(line: 11, column: 7, scope: !10)
+!38 = !DILocation(line: 12, column: 7, scope: !10)
+!39 = !DILocation(line: 12, column: 3, scope: !10)
+!40 = !DILocalVariable(name: "v", scope: !10, file: !1, line: 13, type: !13)
+!41 = !DILocation(line: 13, column: 7, scope: !10)
+!42 = !DILocation(line: 13, column: 11, scope: !10)
+!43 = !DILocation(line: 14, column: 4, scope: !10)
+!44 = !DILocation(line: 15, column: 4, scope: !10)
+!45 = !DILocation(line: 16, column: 7, scope: !10)
+!46 = !DILocation(line: 16, column: 3, scope: !10)
+!47 = !DILocation(line: 17, column: 1, scope: !10)
+!48 = distinct !DISubprogram(name: "fn0", scope: !1, file: !1, line: 19, type: !11, scopeLine: 19, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
+!49 = !DILocalVariable(name: "x", arg: 1, scope: !48, file: !1, line: 19, type: !13)
+!50 = !DILocation(line: 19, column: 40, scope: !48)
+!51 = !DILocalVariable(name: "y", arg: 2, scope: !48, file: !1, line: 19, type: !13)
+!52 = !DILocation(line: 19, column: 47, scope: !48)
+!53 = !DILocalVariable(name: "u", scope: !48, file: !1, line: 20, type: !13)
+!54 = !DILocation(line: 20, column: 7, scope: !48)
+!55 = !DILocation(line: 20, column: 11, scope: !48)
+!56 = !DILocation(line: 20, column: 15, scope: !48)
+!57 = !DILocation(line: 20, column: 13, scope: !48)
+!58 = !DILocalVariable(name: "a", scope: !48, file: !1, line: 21, type: !13)
+!59 = !DILocation(line: 21, column: 7, scope: !48)
+!60 = !DILocation(line: 22, column: 7, scope: !61)
+!61 = distinct !DILexicalBlock(scope: !48, file: !1, line: 22, column: 7)
+!62 = !DILocation(line: 22, column: 9, scope: !61)
+!63 = !DILocation(line: 23, column: 10, scope: !64)
+!64 = distinct !DILexicalBlock(scope: !61, file: !1, line: 22, column: 14)
+!65 = !DILocation(line: 23, column: 7, scope: !64)
+!66 = !DILocation(line: 24, column: 7, scope: !64)
+!67 = !DILocation(line: 25, column: 9, scope: !64)
+!68 = !DILocation(line: 25, column: 5, scope: !64)
+!69 = !DILocation(line: 26, column: 3, scope: !64)
+!70 = !DILocation(line: 26, column: 14, scope: !71)
+!71 = distinct !DILexicalBlock(scope: !61, file: !1, line: 26, column: 14)
+!72 = !DILocation(line: 26, column: 16, scope: !71)
+!73 = !DILocalVariable(name: "v", scope: !74, file: !1, line: 27, type: !13)
+!74 = distinct !DILexicalBlock(scope: !71, file: !1, line: 26, column: 21)
+!75 = !DILocation(line: 27, column: 9, scope: !74)
+!76 = !DILocation(line: 27, column: 13, scope: !74)
+!77 = !DILocation(line: 28, column: 6, scope: !74)
+!78 = !DILocation(line: 29, column: 3, scope: !74)
+!79 = !DILocation(line: 30, column: 4, scope: !48)
+!80 = !DILocation(line: 31, column: 7, scope: !48)
+!81 = !DILocation(line: 31, column: 3, scope: !48)
+!82 = !DILocation(line: 32, column: 1, scope: !48)
+!83 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 34, type: !84, scopeLine: 34, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
+!84 = !DISubroutineType(types: !85)
+!85 = !{!13}
+!86 = !DILocalVariable(name: "l", scope: !83, file: !1, line: 35, type: !13)
+!87 = !DILocation(line: 35, column: 7, scope: !83)
+!88 = !DILocalVariable(name: "k", scope: !83, file: !1, line: 35, type: !13)
+!89 = !DILocation(line: 35, column: 10, scope: !83)
+!90 = !DILocation(line: 36, column: 3, scope: !83)
+!91 = !DILocation(line: 37, column: 3, scope: !83)
+!92 = !DILocation(line: 38, column: 7, scope: !83)
+!93 = !DILocation(line: 38, column: 10, scope: !83)
+!94 = !DILocation(line: 38, column: 3, scope: !83)
+!95 = !DILocation(line: 39, column: 7, scope: !83)
+!96 = !DILocation(line: 39, column: 10, scope: !83)
+!97 = !DILocation(line: 39, column: 3, scope: !83)
+!98 = !DILocation(line: 40, column: 3, scope: !83)

diff  --git a/llvm/test/tools/llvm-dwarfdump/X86/coverage.test b/llvm/test/tools/llvm-dwarfdump/X86/coverage.test
index 5539ef93c9999..7ff1caa0359e4 100644
--- a/llvm/test/tools/llvm-dwarfdump/X86/coverage.test
+++ b/llvm/test/tools/llvm-dwarfdump/X86/coverage.test
@@ -5,20 +5,45 @@ RUN: llvm-dwarfdump --show-variable-coverage %t.o | FileCheck %s
 
 CHECK:      Variable coverage statistics:
 CHECK-NEXT: Function InlChain Variable Decl LinesCovered
-CHECK-NEXT: f k test.c:20 5
-CHECK-NEXT: f l test.c:20 5
+CHECK-NEXT: f k test.c:35 6
+CHECK-NEXT: f l test.c:35 6
+CHECK-NEXT: fn0 a test.c:21 12
+CHECK-NEXT: fn0 u test.c:20 12
+CHECK-NEXT: fn0 v test.c:27 2
+CHECK-NEXT: fn0 x test.c:19 12
+CHECK-NEXT: fn0 y test.c:19 12
 CHECK-NEXT: fn1 a test.c:11 14
 CHECK-NEXT: fn1 u test.c:4 14
 CHECK-NEXT: fn1 v test.c:13 14
 CHECK-NEXT: fn1 x test.c:3 14
 CHECK-NEXT: fn1 y test.c:3 14
 
+RUN: llvm-dwarfdump --show-variable-coverage %t-opt.o | FileCheck %s --check-prefix=OPT
+
+OPT:      Variable coverage statistics:
+OPT-NEXT: Function InlChain Variable Decl LinesCovered
+OPT-NEXT: f k test.c:35 6
+OPT-NEXT: f l test.c:35 6
+OPT-NEXT: fn0 a test.c:21 1
+OPT-NEXT: fn0 u test.c:20 5
+OPT-NEXT: fn0 x test.c:19 7
+OPT-NEXT: fn0 y test.c:19 7
+OPT-NEXT: fn1 a test.c:11 5
+OPT-NEXT: fn1 u test.c:4 1
+OPT-NEXT: fn1 v test.c:13 0
+OPT-NEXT: fn1 x test.c:3 7
+OPT-NEXT: fn1 y test.c:3 7
+
 RUN: llvm-dwarfdump --show-variable-coverage --combine-inline-variable-instances %t-opt.o | FileCheck %s --check-prefix=COMBINE
 
 COMBINE:      Variable coverage statistics:
 COMBINE-NEXT: Function InstanceCount Variable Decl LinesCovered
-COMBINE-NEXT: f 1 k test.c:20 5
-COMBINE-NEXT: f 1 l test.c:20 5
+COMBINE-NEXT: f 1 k test.c:35 6
+COMBINE-NEXT: f 1 l test.c:35 6
+COMBINE-NEXT: fn0 1 a test.c:21 1
+COMBINE-NEXT: fn0 1 u test.c:20 5
+COMBINE-NEXT: fn0 1 x test.c:19 7
+COMBINE-NEXT: fn0 1 y test.c:19 7
 COMBINE-NEXT: fn1 1 a test.c:11 5
 COMBINE-NEXT: fn1 1 u test.c:4 1
 COMBINE-NEXT: fn1 1 v test.c:13 0
@@ -29,8 +54,13 @@ RUN: llvm-dwarfdump --show-variable-coverage --coverage-baseline=%t.o %t-opt.o |
 
 BASELINE:      Variable coverage statistics:
 BASELINE-NEXT: Function InlChain Variable Decl LinesCovered Baseline CoveredRatio LinesPresent LinesPresentRatio
-BASELINE-NEXT: f k test.c:20 5 5 1 5 1
-BASELINE-NEXT: f l test.c:20 5 5 1 5 1
+BASELINE-NEXT: f k test.c:35 6 6 1 6 1
+BASELINE-NEXT: f l test.c:35 6 6 1 6 1
+BASELINE-NEXT: fn0 a test.c:21 1 12 0.0833 7 0.583
+BASELINE-NEXT: fn0 u test.c:20 5 12 0.417 7 0.583
+BASELINE-NEXT: fn0 v test.c:27 0 2 0 0 0
+BASELINE-NEXT: fn0 x test.c:19 7 12 0.583 7 0.583
+BASELINE-NEXT: fn0 y test.c:19 7 12 0.583 7 0.583
 BASELINE-NEXT: fn1 a test.c:11 5 14 0.357 7 0.5
 BASELINE-NEXT: fn1 u test.c:4 1 14 0.0714 7 0.5
 BASELINE-NEXT: fn1 v test.c:13 0 14 0 7 0.5
@@ -41,8 +71,13 @@ RUN: llvm-dwarfdump --show-variable-coverage --variable-coverage-bitcode-file=%S
 
 BITCODE:      Variable coverage statistics:
 BITCODE-NEXT: Function InlChain Variable Decl LinesCovered
-BITCODE-NEXT: f k test.c:20 2
-BITCODE-NEXT: f l test.c:20 3
+BITCODE-NEXT: f k test.c:35 3
+BITCODE-NEXT: f l test.c:35 4
+BITCODE-NEXT: fn0 a test.c:21 2
+BITCODE-NEXT: fn0 u test.c:20 10
+BITCODE-NEXT: fn0 v test.c:27 1
+BITCODE-NEXT: fn0 x test.c:19 11
+BITCODE-NEXT: fn0 y test.c:19 11
 BITCODE-NEXT: fn1 a test.c:11 6
 BITCODE-NEXT: fn1 u test.c:4 12
 BITCODE-NEXT: fn1 v test.c:13 4
@@ -53,10 +88,49 @@ RUN: llvm-dwarfdump --show-variable-coverage --variable-coverage-bitcode-file=%S
 
 BASELINE-BITCODE:      Variable coverage statistics:
 BASELINE-BITCODE-NEXT: Function InlChain Variable Decl LinesCovered Baseline CoveredRatio LinesPresent LinesPresentRatio
-BASELINE-BITCODE-NEXT: f k test.c:20 2 2 1 2 1
-BASELINE-BITCODE-NEXT: f l test.c:20 3 3 1 3 1
+BASELINE-BITCODE-NEXT: f k test.c:35 3 3 1 3 1
+BASELINE-BITCODE-NEXT: f l test.c:35 4 4 1 4 1
+BASELINE-BITCODE-NEXT: fn0 a test.c:21 1 2 0.5 1 0.5
+BASELINE-BITCODE-NEXT: fn0 u test.c:20 5 10 0.5 5 0.5
+BASELINE-BITCODE-NEXT: fn0 v test.c:27 0 1 0 0 0
+BASELINE-BITCODE-NEXT: fn0 x test.c:19 6 11 0.545 6 0.545
+BASELINE-BITCODE-NEXT: fn0 y test.c:19 6 11 0.545 6 0.545
 BASELINE-BITCODE-NEXT: fn1 a test.c:11 3 6 0.5 3 0.5
 BASELINE-BITCODE-NEXT: fn1 u test.c:4 1 12 0.0833 5 0.417
 BASELINE-BITCODE-NEXT: fn1 v test.c:13 0 4 0 2 0.5
 BASELINE-BITCODE-NEXT: fn1 x test.c:3 6 13 0.462 6 0.462
 BASELINE-BITCODE-NEXT: fn1 y test.c:3 6 13 0.462 6 0.462
+
+RUN: llvm-dwarfdump --show-variable-coverage --variable-coverage-maybe-undefined --variable-coverage-bitcode-file=%S/Inputs/coverage.ll %t.o | FileCheck %s --check-prefix=MAYBE-UNDEFINED
+
+MAYBE-UNDEFINED:      Variable coverage statistics:
+MAYBE-UNDEFINED-NEXT: Function InlChain Variable Decl LinesCovered
+MAYBE-UNDEFINED-NEXT: f k test.c:35 3
+MAYBE-UNDEFINED-NEXT: f l test.c:35 4
+MAYBE-UNDEFINED-NEXT: fn0 a test.c:21 5
+MAYBE-UNDEFINED-NEXT: fn0 u test.c:20 10
+MAYBE-UNDEFINED-NEXT: fn0 v test.c:27 1
+MAYBE-UNDEFINED-NEXT: fn0 x test.c:19 11
+MAYBE-UNDEFINED-NEXT: fn0 y test.c:19 11
+MAYBE-UNDEFINED-NEXT: fn1 a test.c:11 6
+MAYBE-UNDEFINED-NEXT: fn1 u test.c:4 12
+MAYBE-UNDEFINED-NEXT: fn1 v test.c:13 4
+MAYBE-UNDEFINED-NEXT: fn1 x test.c:3 13
+MAYBE-UNDEFINED-NEXT: fn1 y test.c:3 13
+
+RUN: llvm-dwarfdump --show-variable-coverage --variable-coverage-maybe-undefined --variable-coverage-bitcode-file=%S/Inputs/coverage.ll --coverage-baseline %t.o %t-opt.o | FileCheck %s --check-prefix=MAYBE-UNDEFINED-BASELINE
+
+MAYBE-UNDEFINED-BASELINE:      Variable coverage statistics:
+MAYBE-UNDEFINED-BASELINE-NEXT: Function InlChain Variable Decl LinesCovered Baseline CoveredRatio LinesPresent LinesPresentRatio
+MAYBE-UNDEFINED-BASELINE-NEXT: f k test.c:35 3 3 1 3 1
+MAYBE-UNDEFINED-BASELINE-NEXT: f l test.c:35 4 4 1 4 1
+MAYBE-UNDEFINED-BASELINE-NEXT: fn0 a test.c:21 1 5 0.2 3 0.6
+MAYBE-UNDEFINED-BASELINE-NEXT: fn0 u test.c:20 5 10 0.5 5 0.5
+MAYBE-UNDEFINED-BASELINE-NEXT: fn0 v test.c:27 0 1 0 0 0
+MAYBE-UNDEFINED-BASELINE-NEXT: fn0 x test.c:19 6 11 0.545 6 0.545
+MAYBE-UNDEFINED-BASELINE-NEXT: fn0 y test.c:19 6 11 0.545 6 0.545
+MAYBE-UNDEFINED-BASELINE-NEXT: fn1 a test.c:11 3 6 0.5 3 0.5
+MAYBE-UNDEFINED-BASELINE-NEXT: fn1 u test.c:4 1 12 0.0833 5 0.417
+MAYBE-UNDEFINED-BASELINE-NEXT: fn1 v test.c:13 0 4 0 2 0.5
+MAYBE-UNDEFINED-BASELINE-NEXT: fn1 x test.c:3 6 13 0.462 6 0.462
+MAYBE-UNDEFINED-BASELINE-NEXT: fn1 y test.c:3 6 13 0.462 6 0.462

diff  --git a/llvm/tools/llvm-dwarfdump/Coverage.cpp b/llvm/tools/llvm-dwarfdump/Coverage.cpp
index 8c525296a8c82..295f3ce3785b4 100644
--- a/llvm/tools/llvm-dwarfdump/Coverage.cpp
+++ b/llvm/tools/llvm-dwarfdump/Coverage.cpp
@@ -159,14 +159,17 @@ computeVariableCoverage(DWARFDie VariableDIE,
           // LineTable::getFileNameByIndex can return absolute paths even when
           // relative paths are requested, so search for keys that end with this
           // path as well.
+          SmallVector<std::pair<StringRef, std::optional<uint16_t>>> NewEntries;
           for (const auto &NameEntry : FileNameMap) {
             auto Name = NameEntry.first();
             if (Name.find(L.first, Name.size() - L.first.size()) !=
                 std::string_view::npos) {
-              FileNameMap.insert({L.first, NameEntry.second});
+              NewEntries.push_back({L.first, NameEntry.second});
               IndexLines.insert({*NameEntry.second, L.second});
             }
           }
+          for (const auto &E : NewEntries)
+            FileNameMap.insert(E);
           if (FileNameMap.find(L.first) == FileNameMap.end()) {
             assert(0 && "Files found in bitcode but not in DWARF");
             FileNameMap.insert({L.first, std::nullopt});
@@ -293,7 +296,7 @@ static void addModuleLines(Instruction *I, VarState &Var,
 }
 
 /// Computes the defined lines of all variables in an IR module.
-static BitcodeLineMap processModule(Module *Mod) {
+static BitcodeLineMap processModule(Module *Mod, bool MaybeUndefined) {
   BitcodeLineMap Result;
   std::vector<VarState> Vars;
   for (auto &F : Mod->functions()) {
@@ -301,10 +304,8 @@ static BitcodeLineMap processModule(Module *Mod) {
     for (auto &BB : F) {
       for (auto &I : BB) {
         for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
-          if (DVR.isKillLocation()) {
-            assert(0 && "Variable in bitcode has been optimized out");
+          if (DVR.isKillLocation())
             continue;
-          }
           if (DVR.isDbgDeclare()) {
             // For #dbg_declare, don't treat the variable as live until we find
             // a store to it.
@@ -346,32 +347,56 @@ static BitcodeLineMap processModule(Module *Mod) {
       SmallPtrSet<BasicBlock *, 8> Visited;
       DenseSet<std::pair<StringRef, uint32_t>> Lines;
 
-      // Visit all basic blocks that are reachable from the entry block without
-      // going through a block that stores to the variable.
-      SmallVector<BasicBlock *> BlocksToVisit{&F.getEntryBlock()};
-      while (!BlocksToVisit.empty()) {
-        BasicBlock *BB = BlocksToVisit.pop_back_val();
-        if (!Visited.insert(BB).second)
-          continue;
+      if (MaybeUndefined) {
+        // Visit all basic blocks that are reachable from a definition.
+        auto B = Var.Definitions.keys();
+        SmallVector<BasicBlock *> BlocksToVisit{B.begin(), B.end()};
+
+        while (!BlocksToVisit.empty()) {
+          BasicBlock *BB = BlocksToVisit.pop_back_val();
+          if (!Visited.insert(BB).second)
+            continue;
 
-        auto I = Var.Definitions.find(BB);
-        if (I != Var.Definitions.end()) {
-          // Block contains a definition: add all lines after it to the set
-          if (I->second != nullptr)
-            addModuleLines(I->second, Var, Lines);
-        } else {
-          // Block does not contain a definition: visit its successors
           auto S = successors(BB);
           BlocksToVisit.append(S.begin(), S.end());
+          // Treat all successor blocks as live throughout.
+          for (auto *BB : S)
+            Var.Definitions.insert_or_assign(BB, &BB->front());
         }
-      }
 
-      // All unvisited basic blocks must only be reachable by going through a
-      // block that stores to the variable, so add lines to the set for all of
-      // their instructions.
-      for (auto &BB : F)
-        if (!Visited.count(&BB))
-          addModuleLines(&*BB.begin(), Var, Lines);
+        // Add lines to the set for all blocks in the definition map.
+        for (auto I : Var.Definitions)
+          if (I.second != nullptr)
+            addModuleLines(I.second, Var, Lines);
+      } else {
+        // Visit all basic blocks that are reachable from the entry block
+        // without going through a block that stores to the variable.
+        SmallVector<BasicBlock *> BlocksToVisit{&F.getEntryBlock()};
+
+        while (!BlocksToVisit.empty()) {
+          BasicBlock *BB = BlocksToVisit.pop_back_val();
+          if (!Visited.insert(BB).second)
+            continue;
+
+          auto I = Var.Definitions.find(BB);
+          if (I != Var.Definitions.end()) {
+            // Block contains a definition: add all lines after it to the set
+            if (I->second != nullptr)
+              addModuleLines(I->second, Var, Lines);
+          } else {
+            // Block does not contain a definition: visit its successors
+            auto S = successors(BB);
+            BlocksToVisit.append(S.begin(), S.end());
+          }
+        }
+
+        // All unvisited basic blocks must only be reachable by going through a
+        // block that stores to the variable, so add lines to the set for all of
+        // their instructions.
+        for (auto &BB : F)
+          if (!Visited.count(&BB))
+            addModuleLines(&BB.front(), Var, Lines);
+      }
 
       BitcodeVarKey Key(F.getName(), Var.DVR.getVariable()->getName());
       Result.emplace(Key, Lines);
@@ -480,7 +505,7 @@ static void displayVariableCoverage(const VarKey &Key, const VarCoverage &Var,
 bool dwarfdump::showVariableCoverage(ObjectFile &Obj, DWARFContext &DICtx,
                                      ObjectFile *BaselineObj,
                                      DWARFContext *BaselineCtx,
-                                     StringRef BitcodeFile,
+                                     StringRef BitcodeFile, bool MaybeUndefined,
                                      bool CombineInstances, raw_ostream &OS) {
   BitcodeLineMap LM;
   LLVMContext Context;
@@ -490,7 +515,7 @@ bool dwarfdump::showVariableCoverage(ObjectFile &Obj, DWARFContext &DICtx,
     if (!Err.getMessage().empty())
       Err.print("llvm-dwarfdump", OS);
     else
-      LM = processModule(Mod.get());
+      LM = processModule(Mod.get(), MaybeUndefined);
   }
 
   BaselineVarMap BaselineVars;
@@ -579,6 +604,10 @@ bool dwarfdump::showVariableCoverage(ObjectFile &Obj, DWARFContext &DICtx,
     }
   }
 
+  for (auto V : BaselineVars)
+    if (!Vars.count(V.first))
+      Vars.insert({V.first, {{}, 0, V.second.size(), 0, 0, 1, false}});
+
   std::pair<VarMap::iterator, VarMap::iterator> Range;
 
   OS << "\nVariable coverage statistics:\nFunction\t"

diff  --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 41f9b1dc88284..638d87bcb982e 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -349,6 +349,13 @@ static opt<std::string>
                      "calculating variable definedness in coverage statistics "
                      "(implies --show-variable-coverage)"),
                 value_desc("filename"), cat(DwarfDumpCategory));
+static opt<bool> MaybeUndefined(
+    "variable-coverage-maybe-undefined",
+    desc("Use with --show-variable-coverage and "
+         "--variable-coverage-bitcode-file to consider variables live if they "
+         "are defined on at least one path (default behaviour is to require a "
+         "variable to be defined on all paths to be counted)"),
+    cat(DwarfDumpCategory));
 static opt<bool> CombineInstances(
     "combine-inline-variable-instances",
     desc(
@@ -995,7 +1002,8 @@ int main(int argc, char **argv) {
       auto showCoverage = [&](ObjectFile &Obj, DWARFContext &DICtx,
                               const Twine &Filename, raw_ostream &OS) {
         return showVariableCoverage(Obj, DICtx, &BaselineObj, &BaselineCtx,
-                                    BitcodeFile, CombineInstances, OS);
+                                    BitcodeFile, MaybeUndefined,
+                                    CombineInstances, OS);
       };
       for (StringRef Object : Objects)
         Success &= handleFile(Object, showCoverage, OutputFile.os());
@@ -1006,7 +1014,7 @@ int main(int argc, char **argv) {
     auto showCoverage = [&](ObjectFile &Obj, DWARFContext &DICtx,
                             const Twine &Filename, raw_ostream &OS) {
       return showVariableCoverage(Obj, DICtx, nullptr, nullptr, BitcodeFile,
-                                  CombineInstances, OS);
+                                  MaybeUndefined, CombineInstances, OS);
     };
     for (StringRef Object : Objects)
       Success &= handleFile(Object, showCoverage, OutputFile.os());

diff  --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
index e9d0433cc3f49..c59eb915c1deb 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h
@@ -42,7 +42,8 @@ bool collectObjectSectionSizes(object::ObjectFile &Obj, DWARFContext &DICtx,
 bool showVariableCoverage(object::ObjectFile &Obj, DWARFContext &DICtx,
                           object::ObjectFile *BaselineObj,
                           DWARFContext *BaselineCtx, StringRef BitcodeFile,
-                          bool CombineInstances, raw_ostream &OS);
+                          bool MaybeUndefined, bool CombineInstances,
+                          raw_ostream &OS);
 } // namespace dwarfdump
 } // namespace llvm
 


        


More information about the llvm-commits mailing list