Skip to content

Globalopt pass produces invalid debug info #100654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 8, 2024
Merged

Conversation

ykhatav
Copy link
Contributor

@ykhatav ykhatav commented Jul 25, 2024

This patch fixes an issue in the GlobalOpt pass where deleting a global variable fails to update the corresponding dbg.value and it references an empty metadata entry. The SalvageDebugInfo() function has been updated to handle dbg.value intrinsic when globals are deleted.

@ykhatav ykhatav changed the title undef dbg val Globalopt pass produces invalid debug info Jul 26, 2024
@ykhatav ykhatav requested review from bwyma and stevemerr July 26, 2024 15:00
@ykhatav ykhatav marked this pull request as ready for review July 26, 2024 15:09
@llvmbot
Copy link
Member

llvmbot commented Jul 26, 2024

@llvm/pr-subscribers-debuginfo
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-llvm-transforms

Author: ykhatav (ykhatav)

Changes

This patch fixes an issue in the GlobalOpt pass where deleting a global variable fails to update the corresponding dbg.value and it references an empty metadata entry. The SalvageDebugInfo() function has been updated to handle dbg.value intrinsic when globals are deleted.


Full diff: https://github.com/llvm/llvm-project/pull/100654.diff

3 Files Affected:

  • (modified) llvm/lib/IR/Metadata.cpp (+6)
  • (modified) llvm/lib/Transforms/IPO/GlobalOpt.cpp (+1)
  • (added) llvm/test/DebugInfo/X86/undef-dbg-val.ll (+35)
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index ae5f5de142328..a91b073f17348 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -346,6 +346,12 @@ void ReplaceableMetadataImpl::SalvageDebugInfo(const Constant &C) {
     MetadataTracking::OwnerTy Owner = Pair.second.first;
     if (!Owner)
       continue;
+    // Check for MetadataAsValue.
+    if (isa<MetadataAsValue *>(Owner)) {
+      cast<MetadataAsValue *>(Owner)->handleChangedMetadata(
+          ValueAsMetadata::get(UndefValue::get(C.getType())));
+      continue;
+    }
     if (!isa<Metadata *>(Owner))
       continue;
     auto *OwnerMD = dyn_cast_if_present<MDNode>(cast<Metadata *>(Owner));
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index ab1e41ebf9a9d..5293a777496bc 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -1338,6 +1338,7 @@ deleteIfDead(GlobalValue &GV,
     if (DeleteFnCallback)
       DeleteFnCallback(*F);
   }
+  ReplaceableMetadataImpl::SalvageDebugInfo(GV);
   GV.eraseFromParent();
   ++NumDeleted;
   return true;
diff --git a/llvm/test/DebugInfo/X86/undef-dbg-val.ll b/llvm/test/DebugInfo/X86/undef-dbg-val.ll
new file mode 100644
index 0000000000000..be386cb14c85f
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/undef-dbg-val.ll
@@ -0,0 +1,35 @@
+; RUN:  opt -S -passes=globalopt <%s | FileCheck %s
+; CHECK: #dbg_value(ptr poison, 
+; CHECK-SAME:    [[VAR:![0-9]+]],
+; CHECK-SAME:    !DIExpression()
+; CHECK: [[VAR]] = !DILocalVariable(name: "_format"
+
+
+; ModuleID = '<stdin>'
+source_filename = "test.cpp"
+
+@_ZZZZ4mainENKUlRN4sycl3_V17handlerEE_clES2_ENKUlvE_clEvE7_format = internal constant [24 x i8] c"Result1: Hello, World!\0A\00", align 16, !dbg !9
+
+; Function Attrs: convergent mustprogress norecurse
+define void  @foo() align 2 !dbg !5 {
+entry:
+  call void @llvm.dbg.value(metadata ptr @_ZZZZ4mainENKUlRN4sycl3_V17handlerEE_clES2_ENKUlvE_clEvE7_format, metadata !11, metadata !DIExpression()), !dbg !12
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.cpp", directory: "/path/to")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 27, type: !6, scopeLine: 27, spFlags: DISPFlagDefinition, unit: !0)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DIGlobalVariableExpression(var: !10, expr: !DIExpression())
+!10 = distinct !DIGlobalVariable(name: "_format", scope: !5, file: !1, line: 49, type: !8, isLocal: true, isDefinition: true)
+!11 = !DILocalVariable(name: "_format", arg: 1, scope: !5, file: !1, line: 79, type: !8)
+!12 = !DILocation(line: 0, scope: !5)

@jmorse
Copy link
Member

jmorse commented Jul 26, 2024

Hi -- using a recent opt (built from 1ebfc81) the test case already produces a #dbg_value with "poison" operand, which suggests this patch isn't having any effect. Perhaps it was recently fixed somewhere else?

@ykhatav
Copy link
Contributor Author

ykhatav commented Jul 26, 2024

Hi -- using a recent opt (built from 1ebfc81) the test case already produces a #dbg_value with "poison" operand, which suggests this patch isn't having any effect. Perhaps it was recently fixed somewhere else?

Sorry for the confusion. The problem only reproduces with the --experimental-debuginfo-iterators=false switch. Looks like the new debug format does not cause this issue. I have updated the test to use this switch. I think we should still keep the code change for backward compatibility.

@dwblaikie dwblaikie requested a review from jmorse July 26, 2024 21:00
@jmorse jmorse requested review from SLTozer and OCHyams July 27, 2024 07:53
@jmorse
Copy link
Member

jmorse commented Jul 27, 2024

Interesting; we were aiming for identical behaviour between the two formats to avoid any uncertainty about it's correctness. I believe we're not aiming to keep support for intrinsic-format in the future, but if there's changing behaviour then this area wants testing and more examination (will examine it more on Monday).

@@ -1338,6 +1338,7 @@ deleteIfDead(GlobalValue &GV,
if (DeleteFnCallback)
DeleteFnCallback(*F);
}
ReplaceableMetadataImpl::SalvageDebugInfo(GV);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line necessary? I believe GV.removeDeadConstantUsers(); should already call this if GV is dead, as part of constantIsDead(..., /*RemoveDeadUsers=*/true).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop responsible for removing dead users and potentially calling constantIsDead() does not execute in this case. It looks like both the iterators I and E are null indicating it does not count debug intrinsic as users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be more appropriate to put this into removeDeadConstantUsers then, after the loop - I think we'd want to perform the salvage step for any constant that dies with debug users regardless of the pass that does it. However, that might end up affecting other tests; if so it can easily be left for another patch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can investigate more and follow up on a different patch if it's a viable approach.

@ykhatav
Copy link
Contributor Author

ykhatav commented Aug 1, 2024

friendly ping!

Copy link
Contributor

@SLTozer SLTozer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delay, been on holiday for a bit. Patch LGTM, with some minor nits and a non-blocking comment/suggestion.

@@ -1338,6 +1338,7 @@ deleteIfDead(GlobalValue &GV,
if (DeleteFnCallback)
DeleteFnCallback(*F);
}
ReplaceableMetadataImpl::SalvageDebugInfo(GV);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be more appropriate to put this into removeDeadConstantUsers then, after the loop - I think we'd want to perform the salvage step for any constant that dies with debug users regardless of the pass that does it. However, that might end up affecting other tests; if so it can easily be left for another patch.

@ykhatav ykhatav merged commit 46bf5d5 into llvm:main Aug 8, 2024
7 checks passed
@ykhatav ykhatav deleted the undef-dbg-val branch August 8, 2024 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants