Skip to content

[GVN] Excluding dead blocks before full redundancy eliminations #88556

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mshockwave
Copy link
Member

Dead blocks will never contribute to non-local load value definitions; keeping it will trigger one of the assertions when we're building SSA form for candidate loads during full redundancy eliminations.

This fixes #88051

Dead blocks will never contribute to non-local load value definitions;
keeping it will trigger one of the assertions when we're building SSA
form for candidate loads during full redundancy eliminations.

This fixes llvm#88051
@llvmbot
Copy link
Member

llvmbot commented Apr 12, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Min-Yih Hsu (mshockwave)

Changes

Dead blocks will never contribute to non-local load value definitions; keeping it will trigger one of the assertions when we're building SSA form for candidate loads during full redundancy eliminations.

This fixes #88051


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/GVN.cpp (+11)
  • (added) llvm/test/Transforms/GVN/pr88051.ll (+91)
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 67fb2a5da3bb71..d5e84bf02a13e1 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -1888,6 +1888,17 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load) {
   // load, then it is fully redundant and we can use PHI insertion to compute
   // its value.  Insert PHIs and remove the fully redundant value now.
   if (UnavailableBlocks.empty()) {
+    // Excluding dead blocks, whose available values are undef. Note that we
+    // can't do this removal for partial redundancy.
+    auto AVEndIt = ValuesPerBlock.end();
+    auto AVBeginIt =
+        llvm::remove_if(ValuesPerBlock, [](const auto &AV) -> bool {
+          return AV.AV.isUndefValue();
+        });
+    ValuesPerBlock.erase(AVBeginIt, AVEndIt);
+    if (ValuesPerBlock.empty())
+      return Changed;
+
     LLVM_DEBUG(dbgs() << "GVN REMOVING NONLOCAL LOAD: " << *Load << '\n');
 
     // Perform PHI construction.
diff --git a/llvm/test/Transforms/GVN/pr88051.ll b/llvm/test/Transforms/GVN/pr88051.ll
new file mode 100644
index 00000000000000..c50545764097ac
--- /dev/null
+++ b/llvm/test/Transforms/GVN/pr88051.ll
@@ -0,0 +1,91 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S --passes='gvn' < %s | FileCheck %s
+
+@g = external global i32
+@e = external global [1 x i32]
+
+define signext i32 @main() nounwind {
+; CHECK-LABEL: define signext i32 @main(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[WHILE_BODY:%.*]]
+; CHECK:       while.body:
+; CHECK-NEXT:    [[DOTPRE15:%.*]] = phi i32 [ [[DOTPRE16:%.*]], [[FOR_END54:%.*]] ], [ undef, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[DOTPRE2:%.*]] = phi i32 [ [[DOTPRE3:%.*]], [[FOR_END54]] ], [ undef, [[ENTRY]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = phi i32 [ [[TMP3:%.*]], [[FOR_END54]] ], [ undef, [[ENTRY]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[TMP4:%.*]], [[FOR_END54]] ], [ undef, [[ENTRY]] ]
+; CHECK-NEXT:    br label [[FOR_BODY3:%.*]]
+; CHECK:       for.body3:
+; CHECK-NEXT:    br i1 false, label [[IF_ELSE:%.*]], label [[FOR_COND6_PREHEADER:%.*]]
+; CHECK:       for.cond6.preheader:
+; CHECK-NEXT:    br label [[IF_END49:%.*]]
+; CHECK:       if.else:
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr null, align 8
+; CHECK-NEXT:    store volatile i32 0, ptr [[TMP2]], align 4
+; CHECK-NEXT:    br i1 false, label [[IF_END:%.*]], label [[FOR_END54SPLITSPLIT:%.*]]
+; CHECK:       if.end:
+; CHECK-NEXT:    br i1 false, label [[IF_END_IF_END49_CRIT_EDGE:%.*]], label [[IF_END_FOR_END54_CRIT_EDGE:%.*]]
+; CHECK:       if.end.if.end49_crit_edge:
+; CHECK-NEXT:    br label [[IF_END49]]
+; CHECK:       if.end.for.end54_crit_edge:
+; CHECK-NEXT:    [[DOTPRE9:%.*]] = sext i32 [[TMP1]] to i64
+; CHECK-NEXT:    br label [[FOR_END54]]
+; CHECK:       if.end49:
+; CHECK-NEXT:    br i1 true, label [[FOR_BODY3]], label [[IF_END49_FOR_END54SPLIT_CRIT_EDGE:%.*]]
+; CHECK:       if.end49.for.end54split_crit_edge:
+; CHECK-NEXT:    [[DOTPRE_PRE:%.*]] = load i32, ptr @g, align 4
+; CHECK-NEXT:    [[DOTPRE8:%.*]] = sext i32 [[DOTPRE_PRE]] to i64
+; CHECK-NEXT:    br label [[FOR_END54SPLIT:%.*]]
+; CHECK:       for.end54splitsplit:
+; CHECK-NEXT:    [[IDXPROM55_PHI_TRANS_INSERT_PHI_TRANS_INSERT:%.*]] = sext i32 [[DOTPRE2]] to i64
+; CHECK-NEXT:    [[ARRAYIDX56_PHI_TRANS_INSERT_PHI_TRANS_INSERT:%.*]] = getelementptr inbounds [1 x i32], ptr @e, i64 0, i64 [[IDXPROM55_PHI_TRANS_INSERT_PHI_TRANS_INSERT]]
+; CHECK-NEXT:    [[DOTPRE1_PRE:%.*]] = load i32, ptr [[ARRAYIDX56_PHI_TRANS_INSERT_PHI_TRANS_INSERT]], align 4
+; CHECK-NEXT:    br label [[FOR_END54SPLIT]]
+; CHECK:       for.end54split:
+; CHECK-NEXT:    [[IDXPROM55_PHI_TRANS_INSERT_PRE_PHI:%.*]] = phi i64 [ [[IDXPROM55_PHI_TRANS_INSERT_PHI_TRANS_INSERT]], [[FOR_END54SPLITSPLIT]] ], [ [[DOTPRE8]], [[IF_END49_FOR_END54SPLIT_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[DOTPRE1:%.*]] = phi i32 [ [[DOTPRE1_PRE]], [[FOR_END54SPLITSPLIT]] ], [ [[DOTPRE15]], [[IF_END49_FOR_END54SPLIT_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[DOTPRE:%.*]] = phi i32 [ [[DOTPRE2]], [[FOR_END54SPLITSPLIT]] ], [ [[DOTPRE_PRE]], [[IF_END49_FOR_END54SPLIT_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[ARRAYIDX56_PHI_TRANS_INSERT:%.*]] = getelementptr inbounds [1 x i32], ptr @e, i64 0, i64 [[IDXPROM55_PHI_TRANS_INSERT_PRE_PHI]]
+; CHECK-NEXT:    br label [[FOR_END54]]
+; CHECK:       for.end54:
+; CHECK-NEXT:    [[IDXPROM55_PRE_PHI:%.*]] = phi i64 [ [[IDXPROM55_PHI_TRANS_INSERT_PRE_PHI]], [[FOR_END54SPLIT]] ], [ [[DOTPRE9]], [[IF_END_FOR_END54_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[DOTPRE16]] = phi i32 [ [[DOTPRE1]], [[FOR_END54SPLIT]] ], [ [[DOTPRE15]], [[IF_END_FOR_END54_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[DOTPRE3]] = phi i32 [ [[DOTPRE]], [[FOR_END54SPLIT]] ], [ [[DOTPRE2]], [[IF_END_FOR_END54_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[TMP3]] = phi i32 [ [[DOTPRE1]], [[FOR_END54SPLIT]] ], [ [[TMP0]], [[IF_END_FOR_END54_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[TMP4]] = phi i32 [ [[DOTPRE]], [[FOR_END54SPLIT]] ], [ [[TMP1]], [[IF_END_FOR_END54_CRIT_EDGE]] ]
+; CHECK-NEXT:    [[ARRAYIDX56:%.*]] = getelementptr inbounds [1 x i32], ptr @e, i64 0, i64 [[IDXPROM55_PRE_PHI]]
+; CHECK-NEXT:    tail call void null(i32 noundef signext [[TMP3]])
+; CHECK-NEXT:    br label [[WHILE_BODY]]
+;
+entry:
+  br label %while.body
+
+while.body:                                       ; preds = %for.end54, %entry
+  br label %for.body3
+
+for.body3:                                        ; preds = %if.end49, %while.body
+  br i1 false, label %if.else, label %for.cond6.preheader
+
+for.cond6.preheader:                              ; preds = %for.body3
+  br label %if.end49
+
+if.else:                                          ; preds = %for.body3
+  %0 = load ptr, ptr null, align 8
+  store volatile i32 0, ptr %0, align 4
+  br i1 false, label %if.end, label %for.end54
+
+if.end:                                           ; preds = %if.else
+  br i1 false, label %if.end49, label %for.end54
+
+if.end49:                                         ; preds = %if.end, %for.cond6.preheader
+  br i1 true, label %for.body3, label %for.end54
+
+for.end54:                                        ; preds = %if.end49, %if.end, %if.else
+  %1 = load i32, ptr @g, align 4
+  %idxprom55 = sext i32 %1 to i64
+  %arrayidx56 = getelementptr inbounds [1 x i32], ptr @e, i64 0, i64 %idxprom55
+  %2 = load i32, ptr %arrayidx56, align 4
+  tail call void null(i32 noundef signext %2)
+  br label %while.body
+}
+

@mshockwave
Copy link
Member Author

To give a little more context on the root cause of #88051, here is the CFG from the reduced test case:
cfg upstream-88051 reduced before main

GVN crashed when it's processing the %.pre1.pre load instruction at block for.end54splitsplit on the right-hand-side of the picture. MemoryDependenceAnalysis indicates that the said load instruction might be congruent with values from block if.else. However, if.else is a dead block as the entire program is just a infinite loop consisting of blocks for.body3, for.cond6.preheader, and if.end49. This triggered the assertion as GVN doesn't allow dead-block-origined values for full redundancy elimination.

This patch filters out any dead blocks from the list of candidate values right before full redundancy elimination.

Copy link
Contributor

@mcberg2021 mcberg2021 left a comment

Choose a reason for hiding this comment

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

These changes look appropriate to me. Second opinion?

@nikic
Copy link
Contributor

nikic commented Apr 17, 2024

Would it be possible / make sense to remove the assertion instead? As I understand it, doing a replacement with undef should be correct here, right?

Looking at the commit that introduced the assertion (3168ab3) I don't think think that the fix being applied here is right. It sounds like maybe DeadBlocks is out of sync?

@mshockwave
Copy link
Member Author

Would it be possible / make sense to remove the assertion instead? As I understand it, doing a replacement with undef should be correct here, right?

Let me give it a try.

Looking at the commit that introduced the assertion (3168ab3) I don't think think that the fix being applied here is right. It sounds like maybe DeadBlocks is out of sync?

I think by saying out of sync you're referring to the on-the-fly edge splitting mentioned in 3168ab3, which I don't think it happen in this case. The if.else block is pretty dead regardless of how we split the edges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:GVN GVN and NewGVN stages (Global value numbering) llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

clang crashes at -O{2,3} on x86_64-linux-gnu: Assertion `!ValuesPerBlock[0].AV.isUndefValue() && "Dead BB dominate this block"' failed
4 participants