Skip to content

Commit f4ebdf0

Browse files
committed
[GVN] Restrict equality propagation for pointers
Reviving https://reviews.llvm.org/D143129 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. Change-Id: I4927ea452734458be028854ef0e5cbcd81955910
1 parent 841a416 commit f4ebdf0

File tree

7 files changed

+354
-61
lines changed

7 files changed

+354
-61
lines changed

llvm/include/llvm/Analysis/Loads.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,15 @@ 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+
bool canReplacePointersInUseIfEqual(const Use &U, const Value *To);
184185
}
185186

186187
#endif

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,18 @@ unsigned replaceDominatedUsesWith(Value *From, Value *To, DominatorTree &DT,
432432
/// the end of the given BasicBlock. Returns the number of replacements made.
433433
unsigned replaceDominatedUsesWith(Value *From, Value *To, DominatorTree &DT,
434434
const BasicBlock *BB);
435+
/// Replace each use of 'From' with 'To' if that use is dominated by
436+
/// the given edge and the callback ShouldReplace returns true. Returns the
437+
/// number of replacements made.
438+
unsigned replaceDominatedUsesWithIf(
439+
Value *From, Value *To, DominatorTree &DT, const BasicBlockEdge &Edge,
440+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace);
441+
/// Replace each use of 'From' with 'To' if that use is dominated by
442+
/// the end of the given BasicBlock and the callback ShouldReplace returns true.
443+
/// Returns the number of replacements made.
444+
unsigned replaceDominatedUsesWithIf(
445+
Value *From, Value *To, DominatorTree &DT, const BasicBlock *BB,
446+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace);
435447

436448
/// Return true if this call calls a gc leaf function.
437449
///

llvm/lib/Analysis/Loads.cpp

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -708,22 +708,66 @@ Value *llvm::FindAvailableLoadedValue(LoadInst *Load, BatchAAResults &AA,
708708
return Available;
709709
}
710710

711-
bool llvm::canReplacePointersIfEqual(Value *A, Value *B, const DataLayout &DL,
712-
Instruction *CtxI) {
713-
Type *Ty = A->getType();
714-
assert(Ty == B->getType() && Ty->isPointerTy() &&
715-
"values must have matching pointer types");
716-
717-
// NOTE: The checks in the function are incomplete and currently miss illegal
718-
// cases! The current implementation is a starting point and the
719-
// implementation should be made stricter over time.
720-
if (auto *C = dyn_cast<Constant>(B)) {
721-
// Do not allow replacing a pointer with a constant pointer, unless it is
722-
// either null or at least one byte is dereferenceable.
723-
APInt OneByte(DL.getPointerTypeSizeInBits(Ty), 1);
724-
return C->isNullValue() ||
725-
isDereferenceableAndAlignedPointer(B, Align(1), OneByte, DL, CtxI);
726-
}
711+
// Returns true if a use is either in an ICmp/PtrToInt or a Phi/Select that only
712+
// feeds into them.
713+
static bool isPointerUseReplacable(const Use &U, int MaxLookup = 6) {
714+
if (MaxLookup == 0)
715+
return false;
716+
717+
const User *User = U.getUser();
718+
if (isa<ICmpInst>(User))
719+
return true;
720+
if (isa<PtrToIntInst>(User))
721+
return true;
722+
if (isa<PHINode, SelectInst>(User) &&
723+
all_of(User->uses(), [&](const Use &Use) {
724+
return isPointerUseReplacable(Use, MaxLookup - 1);
725+
}))
726+
return true;
727+
728+
return false;
729+
}
730+
731+
static const DataLayout &getDLFromVal(const Value *V) {
732+
if (const Argument *A = dyn_cast<Argument>(V))
733+
return A->getParent()->getParent()->getDataLayout();
734+
if (const Instruction *I = dyn_cast<Instruction>(V))
735+
return I->getModule()->getDataLayout();
736+
if (const GlobalValue *GV = dyn_cast<GlobalValue>(V))
737+
return GV->getParent()->getDataLayout();
738+
llvm_unreachable("Unknown Value type");
739+
}
740+
741+
// Returns true if `To` is a null pointer, constant dereferenceable pointer or
742+
// both pointers have the same underlying objects.
743+
static bool isPointerAlwaysReplacable(const Value *From, const Value *To) {
744+
if (isa<ConstantPointerNull>(To))
745+
return true;
746+
if (isa<Constant>(To) &&
747+
isDereferenceablePointer(To, Type::getInt8Ty(To->getContext()),
748+
getDLFromVal(From)))
749+
return true;
750+
if (getUnderlyingObject(From) == getUnderlyingObject(To))
751+
return true;
752+
return false;
753+
}
754+
755+
bool llvm::canReplacePointersInUseIfEqual(const Use &U, const Value *To) {
756+
assert(U->getType() == To->getType() && "values must have matching types");
757+
// Not a pointer, just return true.
758+
if (!To->getType()->isPointerTy())
759+
return true;
760+
761+
if (isPointerAlwaysReplacable(&*U, To))
762+
return true;
763+
return isPointerUseReplacable(U);
764+
}
765+
766+
bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To) {
767+
assert(From->getType() == To->getType() && "values must have matching types");
768+
// Not a pointer, just return true.
769+
if (!From->getType()->isPointerTy())
770+
return true;
727771

728-
return true;
772+
return isPointerAlwaysReplacable(From, To);
729773
}

llvm/lib/Transforms/Scalar/GVN.cpp

Lines changed: 15 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"
@@ -2443,7 +2444,8 @@ bool GVNPass::propagateEquality(Value *LHS, Value *RHS,
24432444
// using the leader table is about compiling faster, not optimizing better).
24442445
// The leader table only tracks basic blocks, not edges. Only add to if we
24452446
// have the simple case where the edge dominates the end.
2446-
if (RootDominatesEnd && !isa<Instruction>(RHS))
2447+
if (RootDominatesEnd && !isa<Instruction>(RHS) &&
2448+
canReplacePointersIfEqual(LHS, RHS))
24472449
addToLeaderTable(LVN, RHS, Root.getEnd());
24482450

24492451
// Replace all occurrences of 'LHS' with 'RHS' everywhere in the scope. As
@@ -2452,14 +2454,18 @@ bool GVNPass::propagateEquality(Value *LHS, Value *RHS,
24522454
if (!LHS->hasOneUse()) {
24532455
unsigned NumReplacements =
24542456
DominatesByEdge
2455-
? replaceDominatedUsesWith(LHS, RHS, *DT, Root)
2456-
: replaceDominatedUsesWith(LHS, RHS, *DT, Root.getStart());
2457-
2458-
Changed |= NumReplacements > 0;
2459-
NumGVNEqProp += NumReplacements;
2460-
// Cached information for anything that uses LHS will be invalid.
2461-
if (MD)
2462-
MD->invalidateCachedPointerInfo(LHS);
2457+
? replaceDominatedUsesWithIf(LHS, RHS, *DT, Root,
2458+
canReplacePointersInUseIfEqual)
2459+
: replaceDominatedUsesWithIf(LHS, RHS, *DT, Root.getStart(),
2460+
canReplacePointersInUseIfEqual);
2461+
2462+
if (NumReplacements > 0) {
2463+
Changed = true;
2464+
NumGVNEqProp += NumReplacements;
2465+
// Cached information for anything that uses LHS will be invalid.
2466+
if (MD)
2467+
MD->invalidateCachedPointerInfo(LHS);
2468+
}
24632469
}
24642470

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

llvm/lib/Transforms/Utils/Local.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3395,15 +3395,18 @@ void llvm::patchReplacementInstruction(Instruction *I, Value *Repl) {
33953395
}
33963396

33973397
template <typename RootType, typename DominatesFn>
3398-
static unsigned replaceDominatedUsesWith(Value *From, Value *To,
3399-
const RootType &Root,
3400-
const DominatesFn &Dominates) {
3398+
static unsigned replaceDominatedUsesWith(
3399+
Value *From, Value *To, const RootType &Root, const DominatesFn &Dominates,
3400+
std::optional<function_ref<bool(const Use &U, const Value *To)>>
3401+
ShouldReplace) {
34013402
assert(From->getType() == To->getType());
34023403

34033404
unsigned Count = 0;
34043405
for (Use &U : llvm::make_early_inc_range(From->uses())) {
34053406
if (!Dominates(Root, U))
34063407
continue;
3408+
if (ShouldReplace.has_value() && !ShouldReplace.value()(U, To))
3409+
continue;
34073410
LLVM_DEBUG(dbgs() << "Replace dominated use of '";
34083411
From->printAsOperand(dbgs());
34093412
dbgs() << "' with " << *To << " in " << *U.getUser() << "\n");
@@ -3434,7 +3437,7 @@ unsigned llvm::replaceDominatedUsesWith(Value *From, Value *To,
34343437
auto Dominates = [&DT](const BasicBlockEdge &Root, const Use &U) {
34353438
return DT.dominates(Root, U);
34363439
};
3437-
return ::replaceDominatedUsesWith(From, To, Root, Dominates);
3440+
return ::replaceDominatedUsesWith(From, To, Root, Dominates, std::nullopt);
34383441
}
34393442

34403443
unsigned llvm::replaceDominatedUsesWith(Value *From, Value *To,
@@ -3443,9 +3446,26 @@ unsigned llvm::replaceDominatedUsesWith(Value *From, Value *To,
34433446
auto Dominates = [&DT](const BasicBlock *BB, const Use &U) {
34443447
return DT.dominates(BB, U);
34453448
};
3446-
return ::replaceDominatedUsesWith(From, To, BB, Dominates);
3449+
return ::replaceDominatedUsesWith(From, To, BB, Dominates, std::nullopt);
3450+
}
3451+
3452+
unsigned llvm::replaceDominatedUsesWithIf(
3453+
Value *From, Value *To, DominatorTree &DT, const BasicBlockEdge &Root,
3454+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace) {
3455+
auto Dominates = [&DT](const BasicBlockEdge &Root, const Use &U) {
3456+
return DT.dominates(Root, U);
3457+
};
3458+
return ::replaceDominatedUsesWith(From, To, Root, Dominates, ShouldReplace);
34473459
}
34483460

3461+
unsigned llvm::replaceDominatedUsesWithIf(
3462+
Value *From, Value *To, DominatorTree &DT, const BasicBlock *BB,
3463+
function_ref<bool(const Use &U, const Value *To)> ShouldReplace) {
3464+
auto Dominates = [&DT](const BasicBlock *BB, const Use &U) {
3465+
return DT.dominates(BB, U);
3466+
};
3467+
return ::replaceDominatedUsesWith(From, To, BB, Dominates, ShouldReplace);
3468+
}
34493469
bool llvm::callsGCLeafFunction(const CallBase *Call,
34503470
const TargetLibraryInfo &TLI) {
34513471
// Check if the function is specifically marked as a gc leaf function.

0 commit comments

Comments
 (0)