[llvm] [SimplifyCFG] Select the first instruction that we can handle in `passingValueIsAlwaysUndefined` (PR #98802)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 14 02:03:46 PDT 2024


https://github.com/DianQK created https://github.com/llvm/llvm-project/pull/98802

Fixes #98799.

>From eba860d1d33b145658157a7ceaf4a079e9db44f3 Mon Sep 17 00:00:00 2001
From: DianQK <dianqk at dianqk.net>
Date: Sat, 13 Jul 2024 21:52:14 +0800
Subject: [PATCH 1/2] Pre-commit test cases

---
 .../pr98799-inline-simplifycfg-ub.ll          | 61 ++++++++++++++++
 .../SimplifyCFG/UnreachableEliminate.ll       | 71 ++++++++++++++++++-
 2 files changed, 130 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll

diff --git a/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll b/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll
new file mode 100644
index 0000000000000..35140cdb6a06c
--- /dev/null
+++ b/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll
@@ -0,0 +1,61 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=inline,simplifycfg -S | FileCheck --check-prefix=CUSTOM %s
+; RUN: opt < %s -O2 -S | FileCheck --check-prefix=O2 %s
+
+define internal ptr @bar(ptr %arg, i1 %arg1) {
+bb:
+  br i1 %arg1, label %bb4, label %bb2
+
+bb2:
+  %i = load ptr, ptr %arg, align 8
+  %i3 = getelementptr inbounds i8, ptr %i, i64 1
+  store ptr %i3, ptr %arg, align 8
+  br label %bb4
+
+bb4:
+  %i5 = phi ptr [ %i, %bb2 ], [ null, %bb ]
+  ret ptr %i5
+}
+
+define i32 @foo(ptr %arg, i1 %arg1) {
+; CUSTOM-LABEL: define i32 @foo(
+; CUSTOM-SAME: ptr [[ARG:%.*]], i1 [[ARG1:%.*]]) {
+; CUSTOM-NEXT:  [[BB:.*]]:
+; CUSTOM-NEXT:    br i1 [[ARG1]], label %[[BAR_EXIT:.*]], label %[[BB2_I:.*]]
+; CUSTOM:       [[BB2_I]]:
+; CUSTOM-NEXT:    [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8
+; CUSTOM-NEXT:    [[I3_I:%.*]] = getelementptr inbounds i8, ptr [[I_I]], i64 1
+; CUSTOM-NEXT:    store ptr [[I3_I]], ptr [[ARG]], align 8
+; CUSTOM-NEXT:    br label %[[BAR_EXIT]]
+; CUSTOM:       [[BAR_EXIT]]:
+; CUSTOM-NEXT:    [[I5_I:%.*]] = phi ptr [ [[I_I]], %[[BB2_I]] ], [ null, %[[BB]] ]
+; CUSTOM-NEXT:    [[I2:%.*]] = icmp ne ptr [[I5_I]], null
+; CUSTOM-NEXT:    call void @llvm.assume(i1 [[I2]])
+; CUSTOM-NEXT:    [[I3:%.*]] = load i32, ptr [[I5_I]], align 4
+; CUSTOM-NEXT:    ret i32 [[I3]]
+;
+; O2-LABEL: define i32 @foo(
+; O2-SAME: ptr nocapture [[ARG:%.*]], i1 [[ARG1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; O2-NEXT:  [[BB:.*]]:
+; O2-NEXT:    br i1 [[ARG1]], label %[[BAR_EXIT:.*]], label %[[BB2_I:.*]]
+; O2:       [[BB2_I]]:
+; O2-NEXT:    [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8
+; O2-NEXT:    [[I3_I:%.*]] = getelementptr inbounds i8, ptr [[I_I]], i64 1
+; O2-NEXT:    store ptr [[I3_I]], ptr [[ARG]], align 8
+; O2-NEXT:    br label %[[BAR_EXIT]]
+; O2:       [[BAR_EXIT]]:
+; O2-NEXT:    [[I5_I:%.*]] = phi ptr [ [[I_I]], %[[BB2_I]] ], [ null, %[[BB]] ]
+; O2-NEXT:    [[I2:%.*]] = icmp ne ptr [[I5_I]], null
+; O2-NEXT:    tail call void @llvm.assume(i1 [[I2]])
+; O2-NEXT:    [[I3:%.*]] = load i32, ptr [[I5_I]], align 4
+; O2-NEXT:    ret i32 [[I3]]
+;
+bb:
+  %i = call ptr @bar(ptr %arg, i1 %arg1)
+  %i2 = icmp ne ptr %i, null
+  call void @llvm.assume(i1 %i2)
+  %i3 = load i32, ptr %i, align 4
+  ret i32 %i3
+}
+
+declare void @llvm.assume(i1)
diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
index ef2d3219cca9b..f928a8991a931 100644
--- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
+++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
@@ -20,7 +20,7 @@ F:
 define void @test2() personality ptr @__gxx_personality_v0 {
 ; CHECK-LABEL: @test2(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @test2() #[[ATTR3:[0-9]+]]
+; CHECK-NEXT:    call void @test2() #[[ATTR4:[0-9]+]]
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -242,6 +242,8 @@ declare ptr @fn_nonnull_deref_arg(ptr nonnull dereferenceable(4) %p)
 declare ptr @fn_nonnull_deref_or_null_arg(ptr nonnull dereferenceable_or_null(4) %p)
 declare ptr @fn_nonnull_arg(ptr nonnull %p)
 declare ptr @fn_noundef_arg(ptr noundef %p)
+declare ptr @fn_ptr_arg(ptr)
+declare ptr @fn_ptr_arg_nounwind_willreturn(ptr) nounwind willreturn
 
 define void @test9(i1 %X, ptr %Y) {
 ; CHECK-LABEL: @test9(
@@ -855,10 +857,75 @@ exit:
   ret i32 %res
 }
 
+; FIXME: From bb to bb5 is UB.
+define i32 @test9_null_user_order_1(ptr %arg, i1 %arg1, ptr %arg2) {
+; CHECK-LABEL: @test9_null_user_order_1(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    br i1 [[ARG1:%.*]], label [[BB5:%.*]], label [[BB3:%.*]]
+; CHECK:       bb3:
+; CHECK-NEXT:    [[I:%.*]] = load ptr, ptr [[ARG:%.*]], align 8
+; CHECK-NEXT:    [[I4:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 1
+; CHECK-NEXT:    store ptr [[I4]], ptr [[ARG]], align 8
+; CHECK-NEXT:    br label [[BB5]]
+; CHECK:       bb5:
+; CHECK-NEXT:    [[I6:%.*]] = phi ptr [ [[I]], [[BB3]] ], [ null, [[BB:%.*]] ]
+; CHECK-NEXT:    [[I7:%.*]] = load i32, ptr [[I6]], align 4
+; CHECK-NEXT:    [[I8:%.*]] = icmp ne ptr [[I6]], [[ARG2:%.*]]
+; CHECK-NEXT:    call void @fn_ptr_arg(i1 [[I8]])
+; CHECK-NEXT:    ret i32 [[I7]]
+;
+bb:
+  br i1 %arg1, label %bb5, label %bb3
+
+bb3:                                              ; preds = %bb
+  %i = load ptr, ptr %arg, align 8
+  %i4 = getelementptr inbounds i8, ptr %i, i64 1
+  store ptr %i4, ptr %arg, align 8
+  br label %bb5
+
+bb5:                                              ; preds = %bb3, %bb
+  %i6 = phi ptr [ %i, %bb3 ], [ null, %bb ]
+  %i7 = load i32, ptr %i6, align 4
+  %i8 = icmp ne ptr %i6, %arg2
+  call void @fn_ptr_arg(i1 %i8)
+  ret i32 %i7
+}
+
+define i32 @test9_null_user_order_2(ptr %arg, i1 %arg1, ptr %arg2) {
+; CHECK-LABEL: @test9_null_user_order_2(
+; CHECK-NEXT:  bb:
+; CHECK-NEXT:    [[TMP0:%.*]] = xor i1 [[ARG1:%.*]], true
+; CHECK-NEXT:    call void @llvm.assume(i1 [[TMP0]])
+; CHECK-NEXT:    [[I:%.*]] = load ptr, ptr [[ARG:%.*]], align 8
+; CHECK-NEXT:    [[I4:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 1
+; CHECK-NEXT:    store ptr [[I4]], ptr [[ARG]], align 8
+; CHECK-NEXT:    [[I8:%.*]] = icmp ne ptr [[I]], [[ARG2:%.*]]
+; CHECK-NEXT:    call void @fn_ptr_arg_nounwind_willreturn(i1 [[I8]])
+; CHECK-NEXT:    [[I7:%.*]] = load i32, ptr [[I]], align 4
+; CHECK-NEXT:    ret i32 [[I7]]
+;
+bb:
+  br i1 %arg1, label %bb5, label %bb3
+
+bb3:                                              ; preds = %bb
+  %i = load ptr, ptr %arg, align 8
+  %i4 = getelementptr inbounds i8, ptr %i, i64 1
+  store ptr %i4, ptr %arg, align 8
+  br label %bb5
+
+bb5:                                              ; preds = %bb3, %bb
+  %i6 = phi ptr [ %i, %bb3 ], [ null, %bb ]
+  %i8 = icmp ne ptr %i6, %arg2
+  call void @fn_ptr_arg_nounwind_willreturn(i1 %i8)
+  %i7 = load i32, ptr %i6, align 4
+  ret i32 %i7
+}
+
 attributes #0 = { null_pointer_is_valid }
 ;.
 ; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
 ; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 ; CHECK: attributes #[[ATTR2:[0-9]+]] = { null_pointer_is_valid }
-; CHECK: attributes #[[ATTR3]] = { nounwind }
+; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind willreturn }
+; CHECK: attributes #[[ATTR4]] = { nounwind }
 ;.

>From b12417f6282ae3c9b32cb08572ece67730459012 Mon Sep 17 00:00:00 2001
From: DianQK <dianqk at dianqk.net>
Date: Sat, 13 Jul 2024 22:23:48 +0800
Subject: [PATCH 2/2] [SimplifyCFG] Select the first instruction that we can
 handle in `passingValueIsAlwaysUndefined`

---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 24 ++++++++++----
 .../pr98799-inline-simplifycfg-ub.ll          | 31 ++++++++-----------
 .../SimplifyCFG/UnreachableEliminate.ll       | 13 +++-----
 3 files changed, 36 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 3fa3c0f1f52b0..a743858855047 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7573,13 +7573,25 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
     return false;
 
   if (C->isNullValue() || isa<UndefValue>(C)) {
-    // Only look at the first use, avoid hurting compile time with long uselists
-    auto *Use = cast<Instruction>(*I->user_begin());
-    // Bail out if Use is not in the same BB as I or Use == I or Use comes
-    // before I in the block. The latter two can be the case if Use is a PHI
-    // node.
-    if (Use->getParent() != I->getParent() || Use == I || Use->comesBefore(I))
+    // Only look at the first use we can hanle, avoid hurting compile time with
+    // long uselists
+    auto FindUse = llvm::find_if(I->users(), [&I](auto *U) {
+      auto *Use = cast<Instruction>(U);
+      // Bail out if Use is not in the same BB as I or Use == I or Use comes
+      // before I in the block. The latter two can be the case if Use is a
+      // PHI node.
+      if (Use->getParent() != I->getParent() || Use == I || Use->comesBefore(I))
+        return false;
+      // Change this list when we want to add new instructions.
+      if (!isa<GetElementPtrInst>(Use) && !isa<ReturnInst>(Use) &&
+          !isa<BitCastInst>(Use) && !isa<LoadInst>(Use) &&
+          !isa<StoreInst>(Use) && !isa<AssumeInst>(Use) && !isa<CallBase>(Use))
+        return false;
+      return true;
+    });
+    if (FindUse == I->user_end())
       return false;
+    auto *Use = cast<Instruction>(*FindUse);
 
     // Now make sure that there are no instructions in between that can alter
     // control flow (eg. calls)
diff --git a/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll b/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll
index 35140cdb6a06c..17073fa198202 100644
--- a/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll
+++ b/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll
@@ -20,34 +20,26 @@ bb4:
 define i32 @foo(ptr %arg, i1 %arg1) {
 ; CUSTOM-LABEL: define i32 @foo(
 ; CUSTOM-SAME: ptr [[ARG:%.*]], i1 [[ARG1:%.*]]) {
-; CUSTOM-NEXT:  [[BB:.*]]:
-; CUSTOM-NEXT:    br i1 [[ARG1]], label %[[BAR_EXIT:.*]], label %[[BB2_I:.*]]
-; CUSTOM:       [[BB2_I]]:
+; CUSTOM-NEXT:  [[BB:.*:]]
+; CUSTOM-NEXT:    [[TMP0:%.*]] = xor i1 [[ARG1]], true
+; CUSTOM-NEXT:    call void @llvm.assume(i1 [[TMP0]])
 ; CUSTOM-NEXT:    [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8
 ; CUSTOM-NEXT:    [[I3_I:%.*]] = getelementptr inbounds i8, ptr [[I_I]], i64 1
 ; CUSTOM-NEXT:    store ptr [[I3_I]], ptr [[ARG]], align 8
-; CUSTOM-NEXT:    br label %[[BAR_EXIT]]
-; CUSTOM:       [[BAR_EXIT]]:
-; CUSTOM-NEXT:    [[I5_I:%.*]] = phi ptr [ [[I_I]], %[[BB2_I]] ], [ null, %[[BB]] ]
-; CUSTOM-NEXT:    [[I2:%.*]] = icmp ne ptr [[I5_I]], null
+; CUSTOM-NEXT:    [[I2:%.*]] = icmp ne ptr [[I_I]], null
 ; CUSTOM-NEXT:    call void @llvm.assume(i1 [[I2]])
-; CUSTOM-NEXT:    [[I3:%.*]] = load i32, ptr [[I5_I]], align 4
+; CUSTOM-NEXT:    [[I3:%.*]] = load i32, ptr [[I_I]], align 4
 ; CUSTOM-NEXT:    ret i32 [[I3]]
 ;
 ; O2-LABEL: define i32 @foo(
 ; O2-SAME: ptr nocapture [[ARG:%.*]], i1 [[ARG1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
-; O2-NEXT:  [[BB:.*]]:
-; O2-NEXT:    br i1 [[ARG1]], label %[[BAR_EXIT:.*]], label %[[BB2_I:.*]]
-; O2:       [[BB2_I]]:
-; O2-NEXT:    [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8
+; O2-NEXT:  [[BB:.*:]]
+; O2-NEXT:    [[TMP0:%.*]] = xor i1 [[ARG1]], true
+; O2-NEXT:    tail call void @llvm.assume(i1 [[TMP0]])
+; O2-NEXT:    [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8, !nonnull [[META0:![0-9]+]], !noundef [[META0]]
 ; O2-NEXT:    [[I3_I:%.*]] = getelementptr inbounds i8, ptr [[I_I]], i64 1
 ; O2-NEXT:    store ptr [[I3_I]], ptr [[ARG]], align 8
-; O2-NEXT:    br label %[[BAR_EXIT]]
-; O2:       [[BAR_EXIT]]:
-; O2-NEXT:    [[I5_I:%.*]] = phi ptr [ [[I_I]], %[[BB2_I]] ], [ null, %[[BB]] ]
-; O2-NEXT:    [[I2:%.*]] = icmp ne ptr [[I5_I]], null
-; O2-NEXT:    tail call void @llvm.assume(i1 [[I2]])
-; O2-NEXT:    [[I3:%.*]] = load i32, ptr [[I5_I]], align 4
+; O2-NEXT:    [[I3:%.*]] = load i32, ptr [[I_I]], align 4
 ; O2-NEXT:    ret i32 [[I3]]
 ;
 bb:
@@ -59,3 +51,6 @@ bb:
 }
 
 declare void @llvm.assume(i1)
+;.
+; O2: [[META0]] = !{}
+;.
diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
index f928a8991a931..c4602e72ecbce 100644
--- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
+++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
@@ -857,20 +857,17 @@ exit:
   ret i32 %res
 }
 
-; FIXME: From bb to bb5 is UB.
+; From bb to bb5 is UB.
 define i32 @test9_null_user_order_1(ptr %arg, i1 %arg1, ptr %arg2) {
 ; CHECK-LABEL: @test9_null_user_order_1(
 ; CHECK-NEXT:  bb:
-; CHECK-NEXT:    br i1 [[ARG1:%.*]], label [[BB5:%.*]], label [[BB3:%.*]]
-; CHECK:       bb3:
+; CHECK-NEXT:    [[TMP0:%.*]] = xor i1 [[ARG1:%.*]], true
+; CHECK-NEXT:    call void @llvm.assume(i1 [[TMP0]])
 ; CHECK-NEXT:    [[I:%.*]] = load ptr, ptr [[ARG:%.*]], align 8
 ; CHECK-NEXT:    [[I4:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 1
 ; CHECK-NEXT:    store ptr [[I4]], ptr [[ARG]], align 8
-; CHECK-NEXT:    br label [[BB5]]
-; CHECK:       bb5:
-; CHECK-NEXT:    [[I6:%.*]] = phi ptr [ [[I]], [[BB3]] ], [ null, [[BB:%.*]] ]
-; CHECK-NEXT:    [[I7:%.*]] = load i32, ptr [[I6]], align 4
-; CHECK-NEXT:    [[I8:%.*]] = icmp ne ptr [[I6]], [[ARG2:%.*]]
+; CHECK-NEXT:    [[I7:%.*]] = load i32, ptr [[I]], align 4
+; CHECK-NEXT:    [[I8:%.*]] = icmp ne ptr [[I]], [[ARG2:%.*]]
 ; CHECK-NEXT:    call void @fn_ptr_arg(i1 [[I8]])
 ; CHECK-NEXT:    ret i32 [[I7]]
 ;



More information about the llvm-commits mailing list