Skip to content
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

[DebugInfo][GVNSink] Fix #77415: GVNSink fails to optimize LLVM IR with debug info #77602

Merged
merged 5 commits into from
May 20, 2024

Conversation

Apochens
Copy link
Contributor

This PR fixes issue #77415 and is revised from PR #77419 .

PR #77419 breaks the newly added test in the same PR on windows, because GVNSink is non-deterministic when sorting BasicBlock* pointers. This is reflected in the failure report.

# | C:\src\llvm-project\llvm\test\Transforms\GVNSink\sink-ignore-dbg-intrinsics.ll:28:10: error: CHECK: expected string not found in input
# | ; CHECK: %a.sink = phi i32 [ %a, %if.then ], [ %b, %if.else ]
# |          ^
# | <stdin>:24:8: note: scanning from here
# | if.end: ; preds = %if.else, %if.then
# |        ^
# | <stdin>:25:2: note: possible intended match here
# |  %b.sink = phi i32 [ %b, %if.else ], [ %a, %if.then ]
# |  ^
# | 
# | Input file: <stdin>
# | Check file: C:\src\llvm-project\llvm\test\Transforms\GVNSink\sink-ignore-dbg-intrinsics.ll

According to the report, what the CheckFile wants to match is the %a.sink, however there is %b.sink. But this mismatch does not mean that this commit is wrong, since the occurrence of either %a.sink or %b.sink is correct. The root cause of this test failure is the strict check rule in the regression test committed.

So I refined the regression test with a more general check rule to only detect whether there is an instruction with suffix .sink in the if.end block. Hope this won't fail the test. If this PR still fails to build, I will close this PR and try to find another right way to fix this.

@llvmbot
Copy link
Collaborator

llvmbot commented Jan 10, 2024

@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-llvm-transforms

Author: Shan Huang (Apochens)

Changes

This PR fixes issue #77415 and is revised from PR #77419 .

PR #77419 breaks the newly added test in the same PR on windows, because GVNSink is non-deterministic when sorting BasicBlock* pointers. This is reflected in the failure report.

# | C:\src\llvm-project\llvm\test\Transforms\GVNSink\sink-ignore-dbg-intrinsics.ll:28:10: error: CHECK: expected string not found in input
# | ; CHECK: %a.sink = phi i32 [ %a, %if.then ], [ %b, %if.else ]
# |          ^
# | &lt;stdin&gt;:24:8: note: scanning from here
# | if.end: ; preds = %if.else, %if.then
# |        ^
# | &lt;stdin&gt;:25:2: note: possible intended match here
# |  %b.sink = phi i32 [ %b, %if.else ], [ %a, %if.then ]
# |  ^
# | 
# | Input file: &lt;stdin&gt;
# | Check file: C:\src\llvm-project\llvm\test\Transforms\GVNSink\sink-ignore-dbg-intrinsics.ll

According to the report, what the CheckFile wants to match is the %a.sink, however there is %b.sink. But this mismatch does not mean that this commit is wrong, since the occurrence of either %a.sink or %b.sink is correct. The root cause of this test failure is the strict check rule in the regression test committed.

So I refined the regression test with a more general check rule to only detect whether there is an instruction with suffix .sink in the if.end block. Hope this won't fail the test. If this PR still fails to build, I will close this PR and try to find another right way to fix this.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/GVNSink.cpp (+3-3)
  • (added) llvm/test/Transforms/GVNSink/sink-ignore-dbg-intrinsics.ll (+86)
diff --git a/llvm/lib/Transforms/Scalar/GVNSink.cpp b/llvm/lib/Transforms/Scalar/GVNSink.cpp
index 2b38831139a580..9db66b3793b8dd 100644
--- a/llvm/lib/Transforms/Scalar/GVNSink.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNSink.cpp
@@ -132,7 +132,7 @@ class LockstepReverseIterator {
         ActiveBlocks.remove(BB);
         continue;
       }
-      Insts.push_back(BB->getTerminator()->getPrevNode());
+      Insts.push_back(BB->getTerminator()->getPrevNonDebugInstruction());
     }
     if (Insts.empty())
       Fail = true;
@@ -168,7 +168,7 @@ class LockstepReverseIterator {
       if (Inst == &Inst->getParent()->front())
         ActiveBlocks.remove(Inst->getParent());
       else
-        NewInsts.push_back(Inst->getPrevNode());
+        NewInsts.push_back(Inst->getPrevNonDebugInstruction());
     }
     if (NewInsts.empty()) {
       Fail = true;
@@ -834,7 +834,7 @@ void GVNSink::sinkLastInstruction(ArrayRef<BasicBlock *> Blocks,
                                   BasicBlock *BBEnd) {
   SmallVector<Instruction *, 4> Insts;
   for (BasicBlock *BB : Blocks)
-    Insts.push_back(BB->getTerminator()->getPrevNode());
+    Insts.push_back(BB->getTerminator()->getPrevNonDebugInstruction());
   Instruction *I0 = Insts.front();
 
   SmallVector<Value *, 4> NewOperands;
diff --git a/llvm/test/Transforms/GVNSink/sink-ignore-dbg-intrinsics.ll b/llvm/test/Transforms/GVNSink/sink-ignore-dbg-intrinsics.ll
new file mode 100644
index 00000000000000..ee6039a0d806ab
--- /dev/null
+++ b/llvm/test/Transforms/GVNSink/sink-ignore-dbg-intrinsics.ll
@@ -0,0 +1,86 @@
+; RUN: opt < %s -passes=gvn-sink -S | FileCheck %s
+
+; Function Attrs: noinline nounwind uwtable
+define dso_local i32 @fun(i32 noundef %a, i32 noundef %b) #0 !dbg !10 {
+entry:
+  tail call void @llvm.dbg.value(metadata i32 %a, metadata !15, metadata !DIExpression()), !dbg !16
+  tail call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression()), !dbg !16
+  %cmp = icmp sgt i32 %b, 10, !dbg !18
+  br i1 %cmp, label %if.then, label %if.else, !dbg !20
+
+if.then:                                          ; preds = %entry
+  %add = add nsw i32 %a, 1, !dbg !21
+  tail call void @llvm.dbg.value(metadata i32 %add, metadata !23, metadata !DIExpression()), !dbg !24
+  %xor = xor i32 %add, 1, !dbg !25
+  tail call void @llvm.dbg.value(metadata i32 %xor, metadata !26, metadata !DIExpression()), !dbg !24
+  tail call void @llvm.dbg.value(metadata i32 %xor, metadata !27, metadata !DIExpression()), !dbg !16
+  br label %if.end, !dbg !28
+
+if.else:                                          ; preds = %entry
+  %add1 = add nsw i32 %b, 1, !dbg !29
+  tail call void @llvm.dbg.value(metadata i32 %add1, metadata !31, metadata !DIExpression()), !dbg !32
+  %xor2 = xor i32 %add1, 1, !dbg !33
+  tail call void @llvm.dbg.value(metadata i32 %xor2, metadata !34, metadata !DIExpression()), !dbg !32
+  tail call void @llvm.dbg.value(metadata i32 %xor2, metadata !27, metadata !DIExpression()), !dbg !16
+  br label %if.end
+
+; CHECK-LABEL: if.end:
+; CHECK: [[SINK:%.*.sink]] = phi i32 [ %a, %if.then ], [ %b, %if.else ]
+; CHECK: %add = add nsw i32 [[SINK]], 1
+; CHECK: %xor = xor i32 %add, 1
+if.end:                                           ; preds = %if.else, %if.then
+  %ret.0 = phi i32 [ %xor, %if.then ], [ %xor2, %if.else ], !dbg !35
+  tail call void @llvm.dbg.value(metadata i32 %ret.0, metadata !27, metadata !DIExpression()), !dbg !16
+  ret i32 %ret.0, !dbg !36
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.value(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!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 18.0.0git (https://github.com/llvm/llvm-project.git 5dfcb3e5d1d16bb4f8fce52b3c089119ed977e7f)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "main.c", directory: "/home/hs/llvm-test", checksumkind: CSK_MD5, checksum: "68c28c3d0877bed08ff43db70c573802")
+!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 = !{i32 7, !"frame-pointer", i32 2}
+!9 = !{!"clang version 18.0.0git (https://github.com/llvm/llvm-project.git 5dfcb3e5d1d16bb4f8fce52b3c089119ed977e7f)"}
+!10 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !14)
+!11 = !DISubroutineType(types: !12)
+!12 = !{!13, !13, !13}
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{}
+!15 = !DILocalVariable(name: "a", arg: 1, scope: !10, file: !1, line: 1, type: !13)
+!16 = !DILocation(line: 0, scope: !10)
+!17 = !DILocalVariable(name: "b", arg: 2, scope: !10, file: !1, line: 1, type: !13)
+!18 = !DILocation(line: 3, column: 11, scope: !19)
+!19 = distinct !DILexicalBlock(scope: !10, file: !1, line: 3, column: 9)
+!20 = !DILocation(line: 3, column: 9, scope: !10)
+!21 = !DILocation(line: 4, column: 20, scope: !22)
+!22 = distinct !DILexicalBlock(scope: !19, file: !1, line: 3, column: 17)
+!23 = !DILocalVariable(name: "a1", scope: !22, file: !1, line: 4, type: !13)
+!24 = !DILocation(line: 0, scope: !22)
+!25 = !DILocation(line: 5, column: 21, scope: !22)
+!26 = !DILocalVariable(name: "a2", scope: !22, file: !1, line: 5, type: !13)
+!27 = !DILocalVariable(name: "ret", scope: !10, file: !1, line: 2, type: !13)
+!28 = !DILocation(line: 7, column: 5, scope: !22)
+!29 = !DILocation(line: 8, column: 20, scope: !30)
+!30 = distinct !DILexicalBlock(scope: !19, file: !1, line: 7, column: 12)
+!31 = !DILocalVariable(name: "b1", scope: !30, file: !1, line: 8, type: !13)
+!32 = !DILocation(line: 0, scope: !30)
+!33 = !DILocation(line: 9, column: 21, scope: !30)
+!34 = !DILocalVariable(name: "b2", scope: !30, file: !1, line: 9, type: !13)
+!35 = !DILocation(line: 0, scope: !19)
+!36 = !DILocation(line: 12, column: 5, scope: !10)
\ No newline at end of file

@dtcxzyw dtcxzyw requested review from nikic and nico February 22, 2024 04:49
@Apochens Apochens changed the title [GVNSink] Fix #77415: GVNSink fails to optimize LLVM IR with debug info [DebugInfo][GVNSink] Fix #77415: GVNSink fails to optimize LLVM IR with debug info Feb 22, 2024
@nikic
Copy link
Contributor

nikic commented Feb 22, 2024

I'm not willing to merge any changes to this pass before the non-determinism issue is fixed, so we can actually test things without fear of breaking the build.

@Apochens
Copy link
Contributor Author

@nikic Hi, I think I have a solution for the non-determinism issue. This issue is caused by llvm::sort(Preds) in function sinkBB. The sorted Preds then is used by LockstepReverseIterator LRI. And in function analyzeInstructionForSinking, ActivePreds assigned by LRI.getActiveBlocks() is appended to Cond.Blocks, which is passed as argument to function sinkLastInstruction:

unsigned GVNSink::sinkBB(BasicBlock *BBEnd) {
   ...
  SmallVector<BasicBlock *, 4> Preds;
  for (auto *B : predecessors(BBEnd)) {
    auto *T = B->getTerminator();
    if (isa<BranchInst>(T) || isa<SwitchInst>(T))
      Preds.push_back(B);
    else
      return 0;
  }
  ...
  llvm::sort(Preds);
  ...
  LockstepReverseIterator LRI(Preds);
  ...
  while (LRI.isValid()) {
    auto Cand = analyzeInstructionForSinking(LRI, InstNum, MemoryInstNum,
                                             NeededPHIs, PHIContents);
    ...
  }
  ...
  for (unsigned I = 0; I < C.NumInstructions; ++I)
    sinkLastInstruction(C.Blocks, InsertBB);

  return C.NumInstructions;
}
std::optional<SinkingInstructionCandidate>
GVNSink::analyzeInstructionForSinking(LockstepReverseIterator &LRI, ...) {
    ...
    auto &ActivePreds = LRI.getActiveBlocks();
    ...
    SinkingInstructionCandidate Cand;
    append_range(Cand.Blocks, ActivePreds);

    return Cand;
}

So the order of basic blocks in function sinkLastInstruction is decided by the memory addresses of BBs in Preds (ie., llvm::sort(Preds)). If the addresses order of BBs change the order changes accordingly.

I think this issue can be solved by sorting BBs according to their names, instead of their memory addresses.

@jryans jryans added the llvm:GVN GVN and NewGVN stages (Global value numbering) label May 9, 2024
@hiraditya hiraditya self-requested a review May 15, 2024 00:49
@hiraditya
Copy link
Collaborator

Hopefully this passes as non-determinism issues were addressed recently. Thanks for fixing the bug with dbg info.

@hiraditya
Copy link
Collaborator

@Apochens do you mind updating the patch, there is a test failure. I think you just need to update the check directives.

@Apochens
Copy link
Contributor Author

@Apochens OK, I'll solve this! Thanks!

Copy link
Collaborator

@hiraditya hiraditya left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for fixing it.

; CHECK: [[SINK:%.*.sink]] = phi i32 [ %a, %if.then ], [ %b, %if.else ]
; CHECK: %add = add nsw i32 [[SINK]], 1
; CHECK: %xor = xor i32 %add, 1
; CHECK: [[SINK:%.*sink]] = phi i32 [ {{.*}} ], [ {{.*}} ]
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: we should have a fixed order here.
maybe swap the PHI operands and see if that passed the check.

Copy link
Collaborator

Choose a reason for hiding this comment

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

or better use the llvm-project/llvm/utils/update_test_checks.py to update the test

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Now the incoming blocks has a fixed order.

@Apochens Apochens requested a review from hiraditya May 17, 2024 14:35
@Apochens
Copy link
Contributor Author

Apochens commented May 20, 2024

@hiraditya I don't have the permission to merge this PR. So, if the PR is ready, could you please help in merging it? Then, I can continue to fix the misleading debug location update.

@dtcxzyw dtcxzyw merged commit f5211d7 into llvm:main May 20, 2024
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
debuginfo llvm:GVN GVN and NewGVN stages (Global value numbering) llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants