[llvm] [ConstraintElim] Check reachability of basic blocks (PR #117118)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 20 23:09:14 PST 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/117118

Closes https://github.com/llvm/llvm-project/issues/117107.


>From e48a70ea4d633a84748ea57b287b092d1d7fa7a0 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 21 Nov 2024 14:08:42 +0800
Subject: [PATCH 1/2] [ConstraintElim] Add pre-commit tests. NFC.

---
 .../ConstraintElimination/unreachable-bb.ll   | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll

diff --git a/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll b/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll
new file mode 100644
index 00000000000000..4e65f5ad385b85
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p constraint-elimination -S %s | FileCheck %s
+
+define void @f(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
+; CHECK-LABEL: define void @f(
+; CHECK-SAME: i32 noundef [[V0:%.*]], i32 noundef [[V1:%.*]], i32 noundef [[V2:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP0:%.*]] = icmp sge i32 [[V0]], [[V1]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2]]
+; CHECK-NEXT:    [[AND1:%.*]] = and i1 [[CMP0]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT:    [[AND2:%.*]] = and i1 [[CMP2]], [[AND1]]
+; CHECK-NEXT:    br i1 [[AND2]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    call void @side_effect()
+; CHECK-NEXT:    br label %[[RETURN]]
+; CHECK:       [[RETURN]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %cmp0 = icmp sge i32 %v0, %v1
+  %cmp1 = icmp sge i32 %v1, %v2
+  %and1 = and i1 %cmp0, %cmp1
+  %cmp2 = icmp slt i32 %v0, %v2
+  %and2 = and i1 %cmp2, %and1
+  br i1 %and2, label %if.then, label %return
+
+if.then:
+  call void @side_effect()
+  br label %return
+
+return:
+  ret void
+}
+
+declare void @side_effect()

>From c6c63fe6e0b0edbe599654240f4a3c0a85fdb547 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 21 Nov 2024 15:06:57 +0800
Subject: [PATCH 2/2] [ConstraintElim] Check reachability of basic blocks

---
 .../Scalar/ConstraintElimination.cpp          | 54 ++++++++++++++++++-
 .../ConstraintElimination/add-nuw.ll          |  2 +-
 .../gep-arithmetic-signed-predicates.ll       |  2 +-
 .../or-implied-by-operands.ll                 |  4 +-
 .../Transforms/ConstraintElimination/or.ll    |  4 +-
 .../ConstraintElimination/sub-nuw.ll          |  6 +--
 .../ConstraintElimination/unreachable-bb.ll   |  2 +-
 7 files changed, 63 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index d03e3a0570cd3f..61bf571c75b438 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -110,7 +110,9 @@ struct FactOrCheck {
                    /// min/mix intrinsic.
     InstCheck,     /// An instruction to simplify (e.g. an overflow math
                    /// intrinsics).
-    UseCheck       /// An use of a compare instruction to simplify.
+    UseCheck,      /// An use of a compare instruction to simplify.
+    BBCheck,       /// A basic block to check whether dominating conditions are
+                   /// satisfiable.
   };
 
   union {
@@ -136,6 +138,10 @@ struct FactOrCheck {
         NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
         Ty(EntryTy::UseCheck) {}
 
+  FactOrCheck(EntryTy Ty, DomTreeNode *DTN)
+      : Inst(DTN->getBlock()->getTerminator()), NumIn(DTN->getDFSNumIn()),
+        NumOut(DTN->getDFSNumOut()), Ty(Ty) {}
+
   FactOrCheck(DomTreeNode *DTN, CmpInst::Predicate Pred, Value *Op0, Value *Op1,
               ConditionTy Precond = ConditionTy())
       : Cond(Pred, Op0, Op1), DoesHold(Precond), NumIn(DTN->getDFSNumIn()),
@@ -155,6 +161,10 @@ struct FactOrCheck {
     return FactOrCheck(DTN, U);
   }
 
+  static FactOrCheck getCheck(DomTreeNode *DTN) {
+    return FactOrCheck(EntryTy::BBCheck, DTN);
+  }
+
   static FactOrCheck getCheck(DomTreeNode *DTN, CallInst *CI) {
     return FactOrCheck(EntryTy::InstCheck, DTN, CI);
   }
@@ -163,6 +173,8 @@ struct FactOrCheck {
     return Ty == EntryTy::InstCheck || Ty == EntryTy::UseCheck;
   }
 
+  bool isBBCheck() const { return Ty == EntryTy::BBCheck; }
+
   Instruction *getContextInst() const {
     assert(!isConditionFact());
     if (Ty == EntryTy::UseCheck)
@@ -1053,6 +1065,11 @@ void State::addInfoForInductions(BasicBlock &BB) {
 }
 
 void State::addInfoFor(BasicBlock &BB) {
+  if (&BB != &BB.getParent()->getEntryBlock()) {
+    if (auto *DTN = DT.getNode(&BB))
+      WorkList.emplace_back(FactOrCheck::getCheck(DTN));
+  }
+
   addInfoForInductions(BB);
 
   // True as long as long as the current instruction is guaranteed to execute.
@@ -1755,6 +1772,41 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
                            DFSInStack);
     }
 
+    // Check whether the basic block is reachable.
+    if (CB.isBBCheck()) {
+      BasicBlock *BB = CB.getContextInst()->getParent();
+      LLVM_DEBUG(dbgs() << "Processing dominating conditions for BB "
+                        << BB->getName() << "\n");
+      auto markBBAsUnreachable = [&] {
+        LLVM_DEBUG(dbgs() << "BB " << BB->getName() << " is unreachable\n");
+        auto &Ctx = BB->getContext();
+        auto *Pred = BB->getSinglePredecessor();
+        auto *PredBranch =
+            Pred ? dyn_cast<BranchInst>(Pred->getTerminator()) : nullptr;
+        // Handle trivial cases with single predecessor.
+        if (PredBranch && PredBranch->isConditional())
+          PredBranch->setCondition(
+              ConstantInt::getBool(Ctx, BB == PredBranch->getSuccessor(1)));
+        else {
+          IRBuilder<> Builder(BB, BB->getFirstInsertionPt());
+          Builder.CreateStore(ConstantInt::getTrue(Ctx),
+                              PoisonValue::get(PointerType::getUnqual(Ctx)));
+        }
+        Changed = true;
+      };
+      auto SignedCS = Info.getCS(true);
+      if (!SignedCS.mayHaveSolution()) {
+        markBBAsUnreachable();
+        continue;
+      }
+      auto UnsignedCS = Info.getCS(false);
+      if (!UnsignedCS.mayHaveSolution()) {
+        markBBAsUnreachable();
+        continue;
+      }
+      continue;
+    }
+
     // For a block, check if any CmpInsts become known based on the current set
     // of constraints.
     if (CB.isCheck()) {
diff --git a/llvm/test/Transforms/ConstraintElimination/add-nuw.ll b/llvm/test/Transforms/ConstraintElimination/add-nuw.ll
index a8a474e6d4502a..ac1840405b94c9 100644
--- a/llvm/test/Transforms/ConstraintElimination/add-nuw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/add-nuw.ll
@@ -455,7 +455,7 @@ define i1 @add_nuw_neg_pr54224_i64(i64 %a) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[NEG2:%.*]] = add nuw i64 [[A:%.*]], -305
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ugt i64 0, [[NEG2]]
-; CHECK-NEXT:    br i1 [[C_1]], label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
+; CHECK-NEXT:    br i1 false, label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
 ; CHECK:       exit.1:
 ; CHECK-NEXT:    ret i1 true
 ; CHECK:       exit.2:
diff --git a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll
index 52094914f6962d..2f3395082e2609 100644
--- a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll
+++ b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-signed-predicates.ll
@@ -612,7 +612,7 @@ define i4 @ptr_N_signed_positive_assume(ptr %src, ptr %lower, ptr %upper, i16 %N
 ; CHECK-NEXT:    [[STEP_POS:%.*]] = icmp sge i16 [[STEP:%.*]], 0
 ; CHECK-NEXT:    [[STEP_SLT_N:%.*]] = icmp slt i16 [[STEP]], [[N]]
 ; CHECK-NEXT:    [[AND_STEP:%.*]] = and i1 false, [[STEP_SLT_N]]
-; CHECK-NEXT:    br i1 [[AND_STEP]], label [[PTR_CHECK:%.*]], label [[EXIT:%.*]]
+; CHECK-NEXT:    br i1 false, label [[PTR_CHECK:%.*]], label [[EXIT:%.*]]
 ; CHECK:       ptr.check:
 ; CHECK-NEXT:    [[SRC_STEP:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i16 [[STEP]]
 ; CHECK-NEXT:    [[CMP_STEP_START:%.*]] = icmp slt ptr [[SRC_STEP]], [[LOWER]]
diff --git a/llvm/test/Transforms/ConstraintElimination/or-implied-by-operands.ll b/llvm/test/Transforms/ConstraintElimination/or-implied-by-operands.ll
index f5c108822b8cdf..d07f29bd1c2086 100644
--- a/llvm/test/Transforms/ConstraintElimination/or-implied-by-operands.ll
+++ b/llvm/test/Transforms/ConstraintElimination/or-implied-by-operands.ll
@@ -7,7 +7,7 @@ define i1 @test_second_or_condition_implied_by_first(i8 %x) {
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[X:%.*]], 10
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp ugt i8 [[X]], 5
 ; CHECK-NEXT:    [[OR:%.*]] = or i1 true, [[T_1]]
-; CHECK-NEXT:    br i1 [[OR]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 true, label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
 ; CHECK-NEXT:    ret i1 false
 ; CHECK:       else:
@@ -32,7 +32,7 @@ define i1 @test_first_or_condition_implied_by_second_ops(i8 %x) {
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[X:%.*]], 10
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp ugt i8 [[X]], 5
 ; CHECK-NEXT:    [[OR:%.*]] = or i1 [[T_1]], true
-; CHECK-NEXT:    br i1 [[OR]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK-NEXT:    br i1 true, label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
 ; CHECK-NEXT:    ret i1 false
 ; CHECK:       else:
diff --git a/llvm/test/Transforms/ConstraintElimination/or.ll b/llvm/test/Transforms/ConstraintElimination/or.ll
index 01b8ca973efa56..44854bb2c76626 100644
--- a/llvm/test/Transforms/ConstraintElimination/or.ll
+++ b/llvm/test/Transforms/ConstraintElimination/or.ll
@@ -126,7 +126,7 @@ define i1 @test_or_chain_ule_1(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
 ; CHECK-NEXT:    [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
 ; CHECK-NEXT:    [[OR_2:%.*]] = or i1 [[OR_1]], [[C_3]]
 ; CHECK-NEXT:    [[OR_3:%.*]] = or i1 [[C_4]], [[OR_2]]
-; CHECK-NEXT:    br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
+; CHECK-NEXT:    br i1 true, label [[BB1:%.*]], label [[EXIT:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp ule i4 [[X]], [[Z]]
 ; CHECK-NEXT:    [[C_6:%.*]] = icmp ule i4 [[X]], [[A]]
@@ -199,7 +199,7 @@ define i1 @test_or_chain_ule_2(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
 ; CHECK-NEXT:    [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
 ; CHECK-NEXT:    [[OR_2:%.*]] = or i1 [[C_3]], [[C_4]]
 ; CHECK-NEXT:    [[OR_3:%.*]] = or i1 [[OR_1]], [[OR_2]]
-; CHECK-NEXT:    br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
+; CHECK-NEXT:    br i1 true, label [[BB1:%.*]], label [[EXIT:%.*]]
 ; CHECK:       bb1:
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp ule i4 [[X]], [[Z]]
 ; CHECK-NEXT:    [[C_6:%.*]] = icmp ule i4 [[X]], [[A]]
diff --git a/llvm/test/Transforms/ConstraintElimination/sub-nuw.ll b/llvm/test/Transforms/ConstraintElimination/sub-nuw.ll
index 5ae559dc2b1b6d..3e76d502e91288 100644
--- a/llvm/test/Transforms/ConstraintElimination/sub-nuw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/sub-nuw.ll
@@ -265,7 +265,7 @@ define i1 @sub_nuw_i16_simp(i16 %a) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[NEG2:%.*]] = sub nuw i16 [[A:%.*]], 305
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ugt i16 0, [[NEG2]]
-; CHECK-NEXT:    br i1 [[C_1]], label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
+; CHECK-NEXT:    br i1 false, label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
 ; CHECK:       exit.1:
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ugt i16 [[A]], 0
 ; CHECK-NEXT:    ret i1 [[C_2]]
@@ -291,7 +291,7 @@ define i1 @sub_nuw_i64_simp(i64 %a) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[NEG2:%.*]] = sub nuw i64 [[A:%.*]], 305
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ugt i64 0, [[NEG2]]
-; CHECK-NEXT:    br i1 [[C_1]], label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
+; CHECK-NEXT:    br i1 false, label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
 ; CHECK:       exit.1:
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ugt i64 [[A]], 0
 ; CHECK-NEXT:    ret i1 [[C_2]]
@@ -317,7 +317,7 @@ define i1 @sub_nuw_neg_i16(i16 %a) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[NEG2:%.*]] = sub nuw i16 [[A:%.*]], -305
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ugt i16 0, [[NEG2]]
-; CHECK-NEXT:    br i1 [[C_1]], label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
+; CHECK-NEXT:    br i1 false, label [[EXIT_1:%.*]], label [[EXIT_2:%.*]]
 ; CHECK:       exit.1:
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ugt i16 [[A]], 0
 ; CHECK-NEXT:    ret i1 [[C_2]]
diff --git a/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll b/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll
index 4e65f5ad385b85..e8ec0ca8d97daa 100644
--- a/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll
+++ b/llvm/test/Transforms/ConstraintElimination/unreachable-bb.ll
@@ -10,7 +10,7 @@ define void @f(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
 ; CHECK-NEXT:    [[AND1:%.*]] = and i1 [[CMP0]], [[CMP1]]
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
 ; CHECK-NEXT:    [[AND2:%.*]] = and i1 [[CMP2]], [[AND1]]
-; CHECK-NEXT:    br i1 [[AND2]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK-NEXT:    br i1 false, label %[[IF_THEN:.*]], label %[[RETURN:.*]]
 ; CHECK:       [[IF_THEN]]:
 ; CHECK-NEXT:    call void @side_effect()
 ; CHECK-NEXT:    br label %[[RETURN]]



More information about the llvm-commits mailing list