Skip to content

Commit ac9e677

Browse files
authored
[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%|
1 parent d99b148 commit ac9e677

File tree

1 file changed

+60
-36
lines changed

1 file changed

+60
-36
lines changed

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7226,84 +7226,108 @@ bool llvm::propagatesPoison(const Use &PoisonOp) {
72267226
}
72277227
}
72287228

7229-
void llvm::getGuaranteedWellDefinedOps(
7230-
const Instruction *I, SmallVectorImpl<const Value *> &Operands) {
7229+
/// Enumerates all operands of \p I that are guaranteed to not be undef or
7230+
/// poison. If the callback \p Handle returns true, stop processing and return
7231+
/// true. Otherwise, return false.
7232+
template <typename CallableT>
7233+
static bool handleGuaranteedWellDefinedOps(const Instruction *I,
7234+
const CallableT &Handle) {
72317235
switch (I->getOpcode()) {
72327236
case Instruction::Store:
7233-
Operands.push_back(cast<StoreInst>(I)->getPointerOperand());
7237+
if (Handle(cast<StoreInst>(I)->getPointerOperand()))
7238+
return true;
72347239
break;
72357240

72367241
case Instruction::Load:
7237-
Operands.push_back(cast<LoadInst>(I)->getPointerOperand());
7242+
if (Handle(cast<LoadInst>(I)->getPointerOperand()))
7243+
return true;
72387244
break;
72397245

72407246
// Since dereferenceable attribute imply noundef, atomic operations
72417247
// also implicitly have noundef pointers too
72427248
case Instruction::AtomicCmpXchg:
7243-
Operands.push_back(cast<AtomicCmpXchgInst>(I)->getPointerOperand());
7249+
if (Handle(cast<AtomicCmpXchgInst>(I)->getPointerOperand()))
7250+
return true;
72447251
break;
72457252

72467253
case Instruction::AtomicRMW:
7247-
Operands.push_back(cast<AtomicRMWInst>(I)->getPointerOperand());
7254+
if (Handle(cast<AtomicRMWInst>(I)->getPointerOperand()))
7255+
return true;
72487256
break;
72497257

72507258
case Instruction::Call:
72517259
case Instruction::Invoke: {
72527260
const CallBase *CB = cast<CallBase>(I);
7253-
if (CB->isIndirectCall())
7254-
Operands.push_back(CB->getCalledOperand());
7255-
for (unsigned i = 0; i < CB->arg_size(); ++i) {
7256-
if (CB->paramHasAttr(i, Attribute::NoUndef) ||
7257-
CB->paramHasAttr(i, Attribute::Dereferenceable) ||
7258-
CB->paramHasAttr(i, Attribute::DereferenceableOrNull))
7259-
Operands.push_back(CB->getArgOperand(i));
7260-
}
7261+
if (CB->isIndirectCall() && Handle(CB->getCalledOperand()))
7262+
return true;
7263+
for (unsigned i = 0; i < CB->arg_size(); ++i)
7264+
if ((CB->paramHasAttr(i, Attribute::NoUndef) ||
7265+
CB->paramHasAttr(i, Attribute::Dereferenceable) ||
7266+
CB->paramHasAttr(i, Attribute::DereferenceableOrNull)) &&
7267+
Handle(CB->getArgOperand(i)))
7268+
return true;
72617269
break;
72627270
}
72637271
case Instruction::Ret:
7264-
if (I->getFunction()->hasRetAttribute(Attribute::NoUndef))
7265-
Operands.push_back(I->getOperand(0));
7272+
if (I->getFunction()->hasRetAttribute(Attribute::NoUndef) &&
7273+
Handle(I->getOperand(0)))
7274+
return true;
72667275
break;
72677276
case Instruction::Switch:
7268-
Operands.push_back(cast<SwitchInst>(I)->getCondition());
7277+
if (Handle(cast<SwitchInst>(I)->getCondition()))
7278+
return true;
72697279
break;
72707280
case Instruction::Br: {
72717281
auto *BR = cast<BranchInst>(I);
7272-
if (BR->isConditional())
7273-
Operands.push_back(BR->getCondition());
7282+
if (BR->isConditional() && Handle(BR->getCondition()))
7283+
return true;
72747284
break;
72757285
}
72767286
default:
72777287
break;
72787288
}
7289+
7290+
return false;
72797291
}
72807292

7281-
void llvm::getGuaranteedNonPoisonOps(const Instruction *I,
7282-
SmallVectorImpl<const Value *> &Operands) {
7283-
getGuaranteedWellDefinedOps(I, Operands);
7293+
void llvm::getGuaranteedWellDefinedOps(
7294+
const Instruction *I, SmallVectorImpl<const Value *> &Operands) {
7295+
handleGuaranteedWellDefinedOps(I, [&](const Value *V) {
7296+
Operands.push_back(V);
7297+
return false;
7298+
});
7299+
}
7300+
7301+
/// Enumerates all operands of \p I that are guaranteed to not be poison.
7302+
template <typename CallableT>
7303+
static bool handleGuaranteedNonPoisonOps(const Instruction *I,
7304+
const CallableT &Handle) {
7305+
if (handleGuaranteedWellDefinedOps(I, Handle))
7306+
return true;
72847307
switch (I->getOpcode()) {
72857308
// Divisors of these operations are allowed to be partially undef.
72867309
case Instruction::UDiv:
72877310
case Instruction::SDiv:
72887311
case Instruction::URem:
72897312
case Instruction::SRem:
7290-
Operands.push_back(I->getOperand(1));
7291-
break;
7313+
return Handle(I->getOperand(1));
72927314
default:
7293-
break;
7315+
return false;
72947316
}
72957317
}
72967318

7319+
void llvm::getGuaranteedNonPoisonOps(const Instruction *I,
7320+
SmallVectorImpl<const Value *> &Operands) {
7321+
handleGuaranteedNonPoisonOps(I, [&](const Value *V) {
7322+
Operands.push_back(V);
7323+
return false;
7324+
});
7325+
}
7326+
72977327
bool llvm::mustTriggerUB(const Instruction *I,
72987328
const SmallPtrSetImpl<const Value *> &KnownPoison) {
7299-
SmallVector<const Value *, 4> NonPoisonOps;
7300-
getGuaranteedNonPoisonOps(I, NonPoisonOps);
7301-
7302-
for (const auto *V : NonPoisonOps)
7303-
if (KnownPoison.count(V))
7304-
return true;
7305-
7306-
return false;
7329+
return handleGuaranteedNonPoisonOps(
7330+
I, [&](const Value *V) { return KnownPoison.count(V); });
73077331
}
73087332

73097333
static bool programUndefinedIfUndefOrPoison(const Value *V,
@@ -7346,9 +7370,9 @@ static bool programUndefinedIfUndefOrPoison(const Value *V,
73467370
if (--ScanLimit == 0)
73477371
break;
73487372

7349-
SmallVector<const Value *, 4> WellDefinedOps;
7350-
getGuaranteedWellDefinedOps(&I, WellDefinedOps);
7351-
if (is_contained(WellDefinedOps, V))
7373+
if (handleGuaranteedWellDefinedOps(&I, [V](const Value *WellDefinedOp) {
7374+
return WellDefinedOp == V;
7375+
}))
73527376
return true;
73537377

73547378
if (!isGuaranteedToTransferExecutionToSuccessor(&I))

0 commit comments

Comments
 (0)