Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,88 @@ bool isGuaranteedToExecuteForEveryIteration(const Instruction *I,
/// getGuaranteedNonPoisonOp.
bool 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 processing and return
/// true. Otherwise, return false.
template <typename CallableT>
bool handleGuaranteedWellDefinedOps(const Instruction *I,
const CallableT &Handle) {
switch (I->getOpcode()) {
case Instruction::Store:
if (Handle(cast<StoreInst>(I)->getPointerOperand()))
return true;
break;

case Instruction::Load:
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:
if (Handle(cast<AtomicCmpXchgInst>(I)->getPointerOperand()))
return true;
break;

case Instruction::AtomicRMW:
if (Handle(cast<AtomicRMWInst>(I)->getPointerOperand()))
return true;
break;

case Instruction::Call:
case Instruction::Invoke: {
const CallBase *CB = cast<CallBase>(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) &&
Handle(I->getOperand(0)))
return true;
break;
case Instruction::Switch:
if (Handle(cast<SwitchInst>(I)->getCondition()))
return true;
break;
case Instruction::Br: {
auto *BR = cast<BranchInst>(I);
if (BR->isConditional() && Handle(BR->getCondition()))
return true;
break;
}
default:
break;
}

return false;
}

/// Enumerates all operands of \p I that are guaranteed to not be poison.
template <typename CallableT>
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:
return Handle(I->getOperand(1));
default:
return false;
}
}

/// Return true if the given instruction must trigger undefined behavior
/// when I is executed with any operands which appear in KnownPoison holding
/// a poison value at the point of execution.
Expand Down
82 changes: 0 additions & 82 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8213,88 +8213,6 @@ 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 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:
if (Handle(cast<StoreInst>(I)->getPointerOperand()))
return true;
break;

case Instruction::Load:
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:
if (Handle(cast<AtomicCmpXchgInst>(I)->getPointerOperand()))
return true;
break;

case Instruction::AtomicRMW:
if (Handle(cast<AtomicRMWInst>(I)->getPointerOperand()))
return true;
break;

case Instruction::Call:
case Instruction::Invoke: {
const CallBase *CB = cast<CallBase>(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) &&
Handle(I->getOperand(0)))
return true;
break;
case Instruction::Switch:
if (Handle(cast<SwitchInst>(I)->getCondition()))
return true;
break;
case Instruction::Br: {
auto *BR = cast<BranchInst>(I);
if (BR->isConditional() && Handle(BR->getCondition()))
return true;
break;
}
default:
break;
}

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:
return Handle(I->getOperand(1));
default:
return false;
}
}

bool llvm::mustTriggerUB(const Instruction *I,
const SmallPtrSetImpl<const Value *> &KnownPoison) {
return handleGuaranteedNonPoisonOps(
Expand Down
Loading
Loading