[llvm] ac9e677 - [ValueTracking][NFC] Early exit when enumerating guaranteed well-defined/non-poison operands. (#82812)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Feb 25 09:53:20 PST 2024
Author: Yingwei Zheng
Date: 2024-02-26T01:53:16+08:00
New Revision: ac9e67756e0157793d565c2cceaf82e4403f58ba
URL: https://github.com/llvm/llvm-project/commit/ac9e67756e0157793d565c2cceaf82e4403f58ba
DIFF: https://github.com/llvm/llvm-project/commit/ac9e67756e0157793d565c2cceaf82e4403f58ba.diff
LOG: [ValueTracking][NFC] Early exit when enumerating guaranteed well-defined/non-poison operands. (#82812)
According to the [coverage
result](https://dtcxzyw.github.io/llvm-opt-benchmark/coverage/home/dtcxzyw/llvm-project/llvm/lib/Analysis/ValueTracking.cpp.html#L7193)
on my benchmark, `llvm::mustTriggerUB` returns true with an average of
35.0M/12.3M=2.85 matches. I think we can stop enumerating when one of
the matches succeeds to avoid filling the temporary buffer
`NonPoisonOps`.
This patch introduces two template functions
`handleGuaranteedWellDefinedOps/handleGuaranteedNonPoisonOps`. They will
pass well-defined/non-poison operands to inlinable callbacks `Handle`.
If the callback returns true, stop processing and return true.
Otherwise, return false.
Compile-time improvement:
https://llvm-compile-time-tracker.com/compare.php?from=13acb3af5ad48e850cf37dcf02270ede3f267bd4&to=2b55f513c1b6dd2732cb79a25f3eaf6c5e4d6619&stat=instructions:u
|stage1-O3|stage1-ReleaseThinLTO|stage1-ReleaseLTO-g|stage1-O0-g|stage2-O3|stage2-O0-g|stage2-clang|
|--|--|--|--|--|--|--|
|-0.03%|-0.04%|-0.06%|-0.03%|-0.05%|+0.03%|-0.02%|
Added:
Modified:
llvm/lib/Analysis/ValueTracking.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 653b3d4ffd9883..de105dfe53ab28 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7226,84 +7226,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 processing and return
+/// true. Otherwise, return false.
+template <typename CallableT>
+static bool handleGuaranteedWellDefinedOps(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) {
+ handleGuaranteedWellDefinedOps(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>
+static 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.
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) {
+ handleGuaranteedNonPoisonOps(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 handleGuaranteedNonPoisonOps(
+ I, [&](const Value *V) { return KnownPoison.count(V); });
}
static bool programUndefinedIfUndefOrPoison(const Value *V,
@@ -7346,9 +7370,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 (handleGuaranteedWellDefinedOps(&I, [V](const Value *WellDefinedOp) {
+ return WellDefinedOp == V;
+ }))
return true;
if (!isGuaranteedToTransferExecutionToSuccessor(&I))
More information about the llvm-commits
mailing list