Skip to content

[ConstraintElim] Add facts about non-poison intrinsics on demand #136558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
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