[llvm] [ValueTracking][NFC] Early exit when enumerating guaranteed well-defined/non-poison operands. (PR #82812)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 23 11:34:44 PST 2024


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/82812

>From 2b55f513c1b6dd2732cb79a25f3eaf6c5e4d6619 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 23 Feb 2024 19:49:14 +0800
Subject: [PATCH 1/2] [ValueTracking] Early exit when enumerating guaranteed
 well-defined/non-poison operands

---
 llvm/lib/Analysis/ValueTracking.cpp | 96 ++++++++++++++++++-----------
 1 file changed, 60 insertions(+), 36 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 04f317228b3ea7..41ab63dcacfc2b 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7211,84 +7211,108 @@ bool llvm::propagatesPoison(const Use &PoisonOp) {
   }
 }
 
-void llvm::getGuaranteedWellDefinedOps(
-    const Instruction *I, SmallVectorImpl<const Value *> &Operands) {
+/// Enumerates all operands of \p I that are guaranteed to not be undef or
+/// poison. If the callback \p Handle returns true, stop iterating and return
+/// true. Otherwise, return false.
+template <typename CallableT>
+bool foreachGuaranteedWellDefinedOps(const Instruction *I,
+                                     const CallableT &Handle) {
   switch (I->getOpcode()) {
     case Instruction::Store:
-      Operands.push_back(cast<StoreInst>(I)->getPointerOperand());
+      if (Handle(cast<StoreInst>(I)->getPointerOperand()))
+        return true;
       break;
 
     case Instruction::Load:
-      Operands.push_back(cast<LoadInst>(I)->getPointerOperand());
+      if (Handle(cast<LoadInst>(I)->getPointerOperand()))
+        return true;
       break;
 
     // Since dereferenceable attribute imply noundef, atomic operations
     // also implicitly have noundef pointers too
     case Instruction::AtomicCmpXchg:
-      Operands.push_back(cast<AtomicCmpXchgInst>(I)->getPointerOperand());
+      if (Handle(cast<AtomicCmpXchgInst>(I)->getPointerOperand()))
+        return true;
       break;
 
     case Instruction::AtomicRMW:
-      Operands.push_back(cast<AtomicRMWInst>(I)->getPointerOperand());
+      if (Handle(cast<AtomicRMWInst>(I)->getPointerOperand()))
+        return true;
       break;
 
     case Instruction::Call:
     case Instruction::Invoke: {
       const CallBase *CB = cast<CallBase>(I);
-      if (CB->isIndirectCall())
-        Operands.push_back(CB->getCalledOperand());
-      for (unsigned i = 0; i < CB->arg_size(); ++i) {
-        if (CB->paramHasAttr(i, Attribute::NoUndef) ||
-            CB->paramHasAttr(i, Attribute::Dereferenceable) ||
-            CB->paramHasAttr(i, Attribute::DereferenceableOrNull))
-          Operands.push_back(CB->getArgOperand(i));
-      }
+      if (CB->isIndirectCall() && Handle(CB->getCalledOperand()))
+        return true;
+      for (unsigned i = 0; i < CB->arg_size(); ++i)
+        if ((CB->paramHasAttr(i, Attribute::NoUndef) ||
+             CB->paramHasAttr(i, Attribute::Dereferenceable) ||
+             CB->paramHasAttr(i, Attribute::DereferenceableOrNull)) &&
+            Handle(CB->getArgOperand(i)))
+          return true;
       break;
     }
     case Instruction::Ret:
-      if (I->getFunction()->hasRetAttribute(Attribute::NoUndef))
-        Operands.push_back(I->getOperand(0));
+      if (I->getFunction()->hasRetAttribute(Attribute::NoUndef) &&
+          Handle(I->getOperand(0)))
+        return true;
       break;
     case Instruction::Switch:
-      Operands.push_back(cast<SwitchInst>(I)->getCondition());
+      if (Handle(cast<SwitchInst>(I)->getCondition()))
+        return true;
       break;
     case Instruction::Br: {
       auto *BR = cast<BranchInst>(I);
-      if (BR->isConditional())
-        Operands.push_back(BR->getCondition());
+      if (BR->isConditional() && Handle(BR->getCondition()))
+        return true;
       break;
     }
     default:
       break;
   }
+
+  return false;
 }
 
-void llvm::getGuaranteedNonPoisonOps(const Instruction *I,
-                                     SmallVectorImpl<const Value *> &Operands) {
-  getGuaranteedWellDefinedOps(I, Operands);
+void llvm::getGuaranteedWellDefinedOps(
+    const Instruction *I, SmallVectorImpl<const Value *> &Operands) {
+  foreachGuaranteedWellDefinedOps(I, [&](const Value *V) {
+    Operands.push_back(V);
+    return false;
+  });
+}
+
+/// Enumerates all operands of \p I that are guaranteed to not be poison.
+template <typename CallableT>
+bool foreachGuaranteedNonPoisonOps(const Instruction *I,
+                                   const CallableT &Handle) {
+  if (foreachGuaranteedWellDefinedOps(I, Handle))
+    return true;
   switch (I->getOpcode()) {
   // Divisors of these operations are allowed to be partially undef.
   case Instruction::UDiv:
   case Instruction::SDiv:
   case Instruction::URem:
   case Instruction::SRem:
-    Operands.push_back(I->getOperand(1));
-    break;
+    return Handle(I->getOperand(1));
   default:
-    break;
+    return false;
   }
 }
 
+void llvm::getGuaranteedNonPoisonOps(const Instruction *I,
+                                     SmallVectorImpl<const Value *> &Operands) {
+  foreachGuaranteedNonPoisonOps(I, [&](const Value *V) {
+    Operands.push_back(V);
+    return false;
+  });
+}
+
 bool llvm::mustTriggerUB(const Instruction *I,
                          const SmallPtrSetImpl<const Value *> &KnownPoison) {
-  SmallVector<const Value *, 4> NonPoisonOps;
-  getGuaranteedNonPoisonOps(I, NonPoisonOps);
-
-  for (const auto *V : NonPoisonOps)
-    if (KnownPoison.count(V))
-      return true;
-
-  return false;
+  return foreachGuaranteedNonPoisonOps(
+      I, [&](const Value *V) { return KnownPoison.count(V); });
 }
 
 static bool programUndefinedIfUndefOrPoison(const Value *V,
@@ -7331,9 +7355,9 @@ static bool programUndefinedIfUndefOrPoison(const Value *V,
       if (--ScanLimit == 0)
         break;
 
-      SmallVector<const Value *, 4> WellDefinedOps;
-      getGuaranteedWellDefinedOps(&I, WellDefinedOps);
-      if (is_contained(WellDefinedOps, V))
+      if (foreachGuaranteedWellDefinedOps(&I, [V](const Value *WellDefinedOp) {
+            return WellDefinedOp == V;
+          }))
         return true;
 
       if (!isGuaranteedToTransferExecutionToSuccessor(&I))

>From ec1035a6ad289c61fab16cdc13b4beec324b889c Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 24 Feb 2024 03:33:53 +0800
Subject: [PATCH 2/2] [ValueTracking] Rename functions.

---
 llvm/lib/Analysis/ValueTracking.cpp | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 41ab63dcacfc2b..fde521b76d7911 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7212,11 +7212,11 @@ bool llvm::propagatesPoison(const Use &PoisonOp) {
 }
 
 /// Enumerates all operands of \p I that are guaranteed to not be undef or
-/// poison. If the callback \p Handle returns true, stop iterating and return
+/// poison. If the callback \p Handle returns true, stop processing and return
 /// true. Otherwise, return false.
 template <typename CallableT>
-bool foreachGuaranteedWellDefinedOps(const Instruction *I,
-                                     const CallableT &Handle) {
+bool handleGuaranteedWellDefinedOps(const Instruction *I,
+                                    const CallableT &Handle) {
   switch (I->getOpcode()) {
     case Instruction::Store:
       if (Handle(cast<StoreInst>(I)->getPointerOperand()))
@@ -7277,7 +7277,7 @@ bool foreachGuaranteedWellDefinedOps(const Instruction *I,
 
 void llvm::getGuaranteedWellDefinedOps(
     const Instruction *I, SmallVectorImpl<const Value *> &Operands) {
-  foreachGuaranteedWellDefinedOps(I, [&](const Value *V) {
+  handleGuaranteedWellDefinedOps(I, [&](const Value *V) {
     Operands.push_back(V);
     return false;
   });
@@ -7285,9 +7285,9 @@ void llvm::getGuaranteedWellDefinedOps(
 
 /// Enumerates all operands of \p I that are guaranteed to not be poison.
 template <typename CallableT>
-bool foreachGuaranteedNonPoisonOps(const Instruction *I,
-                                   const CallableT &Handle) {
-  if (foreachGuaranteedWellDefinedOps(I, Handle))
+bool handleGuaranteedNonPoisonOps(const Instruction *I,
+                                  const CallableT &Handle) {
+  if (handleGuaranteedWellDefinedOps(I, Handle))
     return true;
   switch (I->getOpcode()) {
   // Divisors of these operations are allowed to be partially undef.
@@ -7303,7 +7303,7 @@ bool foreachGuaranteedNonPoisonOps(const Instruction *I,
 
 void llvm::getGuaranteedNonPoisonOps(const Instruction *I,
                                      SmallVectorImpl<const Value *> &Operands) {
-  foreachGuaranteedNonPoisonOps(I, [&](const Value *V) {
+  handleGuaranteedNonPoisonOps(I, [&](const Value *V) {
     Operands.push_back(V);
     return false;
   });
@@ -7311,7 +7311,7 @@ void llvm::getGuaranteedNonPoisonOps(const Instruction *I,
 
 bool llvm::mustTriggerUB(const Instruction *I,
                          const SmallPtrSetImpl<const Value *> &KnownPoison) {
-  return foreachGuaranteedNonPoisonOps(
+  return handleGuaranteedNonPoisonOps(
       I, [&](const Value *V) { return KnownPoison.count(V); });
 }
 
@@ -7355,7 +7355,7 @@ static bool programUndefinedIfUndefOrPoison(const Value *V,
       if (--ScanLimit == 0)
         break;
 
-      if (foreachGuaranteedWellDefinedOps(&I, [V](const Value *WellDefinedOp) {
+      if (handleGuaranteedWellDefinedOps(&I, [V](const Value *WellDefinedOp) {
             return WellDefinedOp == V;
           }))
         return true;



More information about the llvm-commits mailing list