Skip to content

[NFC][SimplifyCFG] Refactor passingValueIsAlwaysUndefined to work on Use #125519

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

Merged
Merged
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
59 changes: 25 additions & 34 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8175,8 +8175,8 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
if (C->isNullValue() || isa<UndefValue>(C)) {
// Only look at the first use we can handle, avoid hurting compile time with
// long uselists
auto FindUse = llvm::find_if(I->users(), [](auto *U) {
auto *Use = cast<Instruction>(U);
auto FindUse = llvm::find_if(I->uses(), [](auto &U) {
auto *Use = cast<Instruction>(U.getUser());
// Change this list when we want to add new instructions.
switch (Use->getOpcode()) {
default:
Expand All @@ -8199,26 +8199,28 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
return true;
}
});
if (FindUse == I->user_end())
if (FindUse == I->use_end())
return false;
auto *Use = cast<Instruction>(*FindUse);
// Bail out if Use is not in the same BB as I or Use == I or Use comes
// before I in the block. The latter two can be the case if Use is a
auto &Use = *FindUse;
auto *User = cast<Instruction>(Use.getUser());
// Bail out if User is not in the same BB as I or User == I or User comes
// before I in the block. The latter two can be the case if User is a
// PHI node.
if (Use->getParent() != I->getParent() || Use == I || Use->comesBefore(I))
if (User->getParent() != I->getParent() || User == I ||
User->comesBefore(I))
return false;

// Now make sure that there are no instructions in between that can alter
// control flow (eg. calls)
auto InstrRange =
make_range(std::next(I->getIterator()), Use->getIterator());
make_range(std::next(I->getIterator()), User->getIterator());
if (any_of(InstrRange, [](Instruction &I) {
return !isGuaranteedToTransferExecutionToSuccessor(&I);
}))
return false;

// Look through GEPs. A load from a GEP derived from NULL is still undefined
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Use))
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(User))
if (GEP->getPointerOperand() == I) {
// The current base address is null, there are four cases to consider:
// getelementptr (TY, null, 0) -> null
Expand All @@ -8235,7 +8237,7 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
}

// Look through return.
if (ReturnInst *Ret = dyn_cast<ReturnInst>(Use)) {
if (ReturnInst *Ret = dyn_cast<ReturnInst>(User)) {
bool HasNoUndefAttr =
Ret->getFunction()->hasRetAttribute(Attribute::NoUndef);
// Return undefined to a noundef return value is undefined.
Expand All @@ -8249,56 +8251,45 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
}

// Load from null is undefined.
if (LoadInst *LI = dyn_cast<LoadInst>(Use))
if (LoadInst *LI = dyn_cast<LoadInst>(User))
if (!LI->isVolatile())
return !NullPointerIsDefined(LI->getFunction(),
LI->getPointerAddressSpace());

// Store to null is undefined.
if (StoreInst *SI = dyn_cast<StoreInst>(Use))
if (StoreInst *SI = dyn_cast<StoreInst>(User))
if (!SI->isVolatile())
return (!NullPointerIsDefined(SI->getFunction(),
SI->getPointerAddressSpace())) &&
SI->getPointerOperand() == I;

// llvm.assume(false/undef) always triggers immediate UB.
if (auto *Assume = dyn_cast<AssumeInst>(Use)) {
if (auto *Assume = dyn_cast<AssumeInst>(User)) {
// Ignore assume operand bundles.
if (I == Assume->getArgOperand(0))
return true;
}

if (auto *CB = dyn_cast<CallBase>(Use)) {
if (auto *CB = dyn_cast<CallBase>(User)) {
if (C->isNullValue() && NullPointerIsDefined(CB->getFunction()))
return false;
// A call to null is undefined.
if (CB->getCalledOperand() == I)
return true;

if (C->isNullValue()) {
for (const llvm::Use &Arg : CB->args())
if (Arg == I) {
unsigned ArgIdx = CB->getArgOperandNo(&Arg);
if (CB->isPassingUndefUB(ArgIdx) &&
CB->paramHasAttr(ArgIdx, Attribute::NonNull)) {
// Passing null to a nonnnull+noundef argument is undefined.
return !PtrValueMayBeModified;
}
}
} else if (isa<UndefValue>(C)) {
if (CB->isArgOperand(&Use)) {
unsigned ArgIdx = CB->getArgOperandNo(&Use);
// Passing null to a nonnnull+noundef argument is undefined.
if (C->isNullValue() && CB->isPassingUndefUB(ArgIdx) &&
CB->paramHasAttr(ArgIdx, Attribute::NonNull))
return !PtrValueMayBeModified;
// Passing undef to a noundef argument is undefined.
for (const llvm::Use &Arg : CB->args())
if (Arg == I) {
unsigned ArgIdx = CB->getArgOperandNo(&Arg);
if (CB->isPassingUndefUB(ArgIdx)) {
// Passing undef to a noundef argument is undefined.
return true;
}
}
if (isa<UndefValue>(C) && CB->isPassingUndefUB(ArgIdx))
return true;
}
}
// Div/Rem by zero is immediate UB
if (match(Use, m_BinOp(m_Value(), m_Specific(I))) && Use->isIntDivRem())
if (match(User, m_BinOp(m_Value(), m_Specific(I))) && User->isIntDivRem())
return true;
}
return false;
Expand Down