Skip to content

Commit b10e4b8

Browse files
authored
[GVN] Restrict equality propagation for pointers (#82458)
This patch does the following: Adds the following functions: - replaceDominatedUsesWithIf() that takes a callback. - canReplacePointersIfEqual(...) returns true if the underlying object is the same, and for null and const dereferencable pointer replacements. - canReplacePointersIfEqualInUse(...) returns true for the above as well as if the use is in icmp/ptrtoint or phi/selects feeding into them. Updates GVN using the functions above so that the pointer replacements are only made using the above API. https://reviews.llvm.org/D143129
1 parent d3f6a88 commit b10e4b8

File tree

7 files changed

+368
-68
lines changed

7 files changed

+368
-68
lines changed

llvm/include/llvm/Analysis/Loads.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,17 @@ Value *findAvailablePtrLoadStore(const MemoryLocation &Loc, Type *AccessTy,
173173
unsigned MaxInstsToScan, BatchAAResults *AA,
174174
bool *IsLoadCSE, unsigned *NumScanedInst);
175175

176-
/// Returns true if a pointer value \p A can be replace with another pointer
177-
/// value \B if they are deemed equal through some means (e.g. information from
176+
/// Returns true if a pointer value \p From can be replaced with another pointer
177+
/// value \To if they are deemed equal through some means (e.g. information from
178178
/// conditions).
179-
/// NOTE: the current implementations is incomplete and unsound. It does not
180-
/// reject all invalid cases yet, but will be made stricter in the future. In
181-
/// particular this means returning true means unknown if replacement is safe.
182-
bool canReplacePointersIfEqual(Value *A, Value *B, const DataLayout &DL,
183-
Instruction *CtxI);
179+
/// NOTE: The current implementation allows replacement in Icmp and PtrToInt
180+
/// instructions, as well as when we are replacing with a null pointer.
181+
/// Additionally it also allows replacement of pointers when both pointers have
182+
/// the same underlying object.
183+
bool canReplacePointersIfEqual(const Value *From, const Value *To,
184+
const DataLayout &DL);
185+
bool canReplacePointersInUseIfEqual(const Use &U, const Value *To,
186+
const DataLayout &DL);
184187
}
185188

186189
#endif

llvm/include/llvm/Transforms/Utils/Local.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,18 @@ unsigned replaceDominatedUsesWith(Value *From, Value *To, DominatorTree &DT,
439439
/// the end of the given BasicBlock. Returns the number of replacements made.
440440
unsigned replaceDominatedUsesWith(Value *From, Value *To, DominatorTree &DT,
441441
const BasicBlock *BB);
442+
/// Replace each use of 'From' with 'To' if that use is dominated by
443+
/// the given edge and the callback ShouldReplace returns true. Returns the
444+
/// number of replacements made.
445+
unsigned replaceDominatedUsesWithIf(
446+
Value *From, Value *To, DominatorTree &DT, const BasicBlockEdge &Edge,
447+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace);
448+
/// Replace each use of 'From' with 'To' if that use is dominated by
449+
/// the end of the given BasicBlock and the callback ShouldReplace returns true.
450+
/// Returns the number of replacements made.
451+
unsigned replaceDominatedUsesWithIf(
452+
Value *From, Value *To, DominatorTree &DT, const BasicBlock *BB,
453+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace);
442454

443455
/// Return true if this call calls a gc leaf function.
444456
///

llvm/lib/Analysis/Loads.cpp

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -710,22 +710,62 @@ Value *llvm::FindAvailableLoadedValue(LoadInst *Load, BatchAAResults &AA,
710710
return Available;
711711
}
712712

713-
bool llvm::canReplacePointersIfEqual(Value *A, Value *B, const DataLayout &DL,
714-
Instruction *CtxI) {
715-
Type *Ty = A->getType();
716-
assert(Ty == B->getType() && Ty->isPointerTy() &&
717-
"values must have matching pointer types");
718-
719-
// NOTE: The checks in the function are incomplete and currently miss illegal
720-
// cases! The current implementation is a starting point and the
721-
// implementation should be made stricter over time.
722-
if (auto *C = dyn_cast<Constant>(B)) {
723-
// Do not allow replacing a pointer with a constant pointer, unless it is
724-
// either null or at least one byte is dereferenceable.
725-
APInt OneByte(DL.getPointerTypeSizeInBits(Ty), 1);
726-
return C->isNullValue() ||
727-
isDereferenceableAndAlignedPointer(B, Align(1), OneByte, DL, CtxI);
713+
// Returns true if a use is either in an ICmp/PtrToInt or a Phi/Select that only
714+
// feeds into them.
715+
static bool isPointerUseReplacable(const Use &U) {
716+
unsigned Limit = 40;
717+
SmallVector<const User *> Worklist({U.getUser()});
718+
SmallPtrSet<const User *, 8> Visited;
719+
720+
while (!Worklist.empty() && --Limit) {
721+
auto *User = Worklist.pop_back_val();
722+
if (!Visited.insert(User).second)
723+
continue;
724+
if (isa<ICmpInst, PtrToIntInst>(User))
725+
continue;
726+
if (isa<PHINode, SelectInst>(User))
727+
Worklist.append(User->user_begin(), User->user_end());
728+
else
729+
return false;
728730
}
729731

730-
return true;
732+
return Limit != 0;
733+
}
734+
735+
// Returns true if `To` is a null pointer, constant dereferenceable pointer or
736+
// both pointers have the same underlying objects.
737+
static bool isPointerAlwaysReplaceable(const Value *From, const Value *To,
738+
const DataLayout &DL) {
739+
// This is not strictly correct, but we do it for now to retain important
740+
// optimizations.
741+
if (isa<ConstantPointerNull>(To))
742+
return true;
743+
if (isa<Constant>(To) &&
744+
isDereferenceablePointer(To, Type::getInt8Ty(To->getContext()), DL))
745+
return true;
746+
if (getUnderlyingObject(From) == getUnderlyingObject(To))
747+
return true;
748+
return false;
749+
}
750+
751+
bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To,
752+
const DataLayout &DL) {
753+
assert(U->getType() == To->getType() && "values must have matching types");
754+
// Not a pointer, just return true.
755+
if (!To->getType()->isPointerTy())
756+
return true;
757+
758+
if (isPointerAlwaysReplaceable(&*U, To, DL))
759+
return true;
760+
return isPointerUseReplacable(U);
761+
}
762+
763+
bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To,
764+
const DataLayout &DL) {
765+
assert(From->getType() == To->getType() && "values must have matching types");
766+
// Not a pointer, just return true.
767+
if (!From->getType()->isPointerTy())
768+
return true;
769+
770+
return isPointerAlwaysReplaceable(From, To, DL);
731771
}

llvm/lib/Transforms/Scalar/GVN.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/Analysis/GlobalsModRef.h"
3434
#include "llvm/Analysis/InstructionPrecedenceTracking.h"
3535
#include "llvm/Analysis/InstructionSimplify.h"
36+
#include "llvm/Analysis/Loads.h"
3637
#include "llvm/Analysis/LoopInfo.h"
3738
#include "llvm/Analysis/MemoryBuiltins.h"
3839
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
@@ -2419,6 +2420,10 @@ bool GVNPass::propagateEquality(Value *LHS, Value *RHS,
24192420
if (isa<Constant>(LHS) || (isa<Argument>(LHS) && !isa<Constant>(RHS)))
24202421
std::swap(LHS, RHS);
24212422
assert((isa<Argument>(LHS) || isa<Instruction>(LHS)) && "Unexpected value!");
2423+
const DataLayout &DL =
2424+
isa<Argument>(LHS)
2425+
? cast<Argument>(LHS)->getParent()->getParent()->getDataLayout()
2426+
: cast<Instruction>(LHS)->getModule()->getDataLayout();
24222427

24232428
// If there is no obvious reason to prefer the left-hand side over the
24242429
// right-hand side, ensure the longest lived term is on the right-hand side,
@@ -2445,23 +2450,32 @@ bool GVNPass::propagateEquality(Value *LHS, Value *RHS,
24452450
// using the leader table is about compiling faster, not optimizing better).
24462451
// The leader table only tracks basic blocks, not edges. Only add to if we
24472452
// have the simple case where the edge dominates the end.
2448-
if (RootDominatesEnd && !isa<Instruction>(RHS))
2453+
if (RootDominatesEnd && !isa<Instruction>(RHS) &&
2454+
canReplacePointersIfEqual(LHS, RHS, DL))
24492455
addToLeaderTable(LVN, RHS, Root.getEnd());
24502456

24512457
// Replace all occurrences of 'LHS' with 'RHS' everywhere in the scope. As
24522458
// LHS always has at least one use that is not dominated by Root, this will
24532459
// never do anything if LHS has only one use.
24542460
if (!LHS->hasOneUse()) {
2461+
// Create a callback that captures the DL.
2462+
auto canReplacePointersCallBack = [&DL](const Use &U, const Value *To) {
2463+
return canReplacePointersInUseIfEqual(U, To, DL);
2464+
};
24552465
unsigned NumReplacements =
24562466
DominatesByEdge
2457-
? replaceDominatedUsesWith(LHS, RHS, *DT, Root)
2458-
: replaceDominatedUsesWith(LHS, RHS, *DT, Root.getStart());
2459-
2460-
Changed |= NumReplacements > 0;
2461-
NumGVNEqProp += NumReplacements;
2462-
// Cached information for anything that uses LHS will be invalid.
2463-
if (MD)
2464-
MD->invalidateCachedPointerInfo(LHS);
2467+
? replaceDominatedUsesWithIf(LHS, RHS, *DT, Root,
2468+
canReplacePointersCallBack)
2469+
: replaceDominatedUsesWithIf(LHS, RHS, *DT, Root.getStart(),
2470+
canReplacePointersCallBack);
2471+
2472+
if (NumReplacements > 0) {
2473+
Changed = true;
2474+
NumGVNEqProp += NumReplacements;
2475+
// Cached information for anything that uses LHS will be invalid.
2476+
if (MD)
2477+
MD->invalidateCachedPointerInfo(LHS);
2478+
}
24652479
}
24662480

24672481
// Now try to deduce additional equalities from this one. For example, if

llvm/lib/Transforms/Utils/Local.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,15 +3444,15 @@ void llvm::patchReplacementInstruction(Instruction *I, Value *Repl) {
34443444
combineMetadataForCSE(ReplInst, I, false);
34453445
}
34463446

3447-
template <typename RootType, typename DominatesFn>
3447+
template <typename RootType, typename ShouldReplaceFn>
34483448
static unsigned replaceDominatedUsesWith(Value *From, Value *To,
34493449
const RootType &Root,
3450-
const DominatesFn &Dominates) {
3450+
const ShouldReplaceFn &ShouldReplace) {
34513451
assert(From->getType() == To->getType());
34523452

34533453
unsigned Count = 0;
34543454
for (Use &U : llvm::make_early_inc_range(From->uses())) {
3455-
if (!Dominates(Root, U))
3455+
if (!ShouldReplace(Root, U))
34563456
continue;
34573457
LLVM_DEBUG(dbgs() << "Replace dominated use of '";
34583458
From->printAsOperand(dbgs());
@@ -3496,6 +3496,26 @@ unsigned llvm::replaceDominatedUsesWith(Value *From, Value *To,
34963496
return ::replaceDominatedUsesWith(From, To, BB, Dominates);
34973497
}
34983498

3499+
unsigned llvm::replaceDominatedUsesWithIf(
3500+
Value *From, Value *To, DominatorTree &DT, const BasicBlockEdge &Root,
3501+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace) {
3502+
auto DominatesAndShouldReplace =
3503+
[&DT, &ShouldReplace, To](const BasicBlockEdge &Root, const Use &U) {
3504+
return DT.dominates(Root, U) && ShouldReplace(U, To);
3505+
};
3506+
return ::replaceDominatedUsesWith(From, To, Root, DominatesAndShouldReplace);
3507+
}
3508+
3509+
unsigned llvm::replaceDominatedUsesWithIf(
3510+
Value *From, Value *To, DominatorTree &DT, const BasicBlock *BB,
3511+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace) {
3512+
auto DominatesAndShouldReplace = [&DT, &ShouldReplace,
3513+
To](const BasicBlock *BB, const Use &U) {
3514+
return DT.dominates(BB, U) && ShouldReplace(U, To);
3515+
};
3516+
return ::replaceDominatedUsesWith(From, To, BB, DominatesAndShouldReplace);
3517+
}
3518+
34993519
bool llvm::callsGCLeafFunction(const CallBase *Call,
35003520
const TargetLibraryInfo &TLI) {
35013521
// Check if the function is specifically marked as a gc leaf function.

0 commit comments

Comments
 (0)