-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LAA] Always use DepCands when grouping runtime checks. #91196
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
base: main
Are you sure you want to change the base?
Changes from all commits
1059eb7
54ebebf
fc9cee7
2943e88
98e7a47
1059148
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -233,6 +233,18 @@ class EquivalenceClasses { | |||||||||||||
return findLeader(TheMapping.find(V)); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/// Erase the class containing \p V. | ||||||||||||||
void eraseClass(const ElemTy &V) { | ||||||||||||||
if (TheMapping.find(V) == TheMapping.end()) | ||||||||||||||
return; | ||||||||||||||
auto LeaderI = findValue(getLeaderValue(V)); | ||||||||||||||
Comment on lines
+238
to
+240
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
for (auto MI = member_begin(LeaderI), ME = member_end(); MI != ME;) { | ||||||||||||||
const auto &ToErase = *MI; | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
++MI; | ||||||||||||||
TheMapping.erase(ToErase); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/// union - Merge the two equivalence sets for the specified values, inserting | ||||||||||||||
/// them if they do not already exist in the equivalence set. | ||||||||||||||
member_iterator unionSets(const ElemTy &V1, const ElemTy &V2) { | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -392,9 +392,10 @@ SmallVector<RuntimePointerCheck, 4> RuntimePointerChecking::generateChecks() { | |
} | ||
|
||
void RuntimePointerChecking::generateChecks( | ||
MemoryDepChecker::DepCandidates &DepCands, bool UseDependencies) { | ||
const MemoryDepChecker &DepChecker, | ||
MemoryDepChecker::DepCandidates &DepCands) { | ||
assert(Checks.empty() && "Checks is not empty"); | ||
groupChecks(DepCands, UseDependencies); | ||
groupChecks(DepChecker, DepCands); | ||
Checks = generateChecks(); | ||
} | ||
|
||
|
@@ -459,7 +460,8 @@ bool RuntimeCheckingPtrGroup::addPointer(unsigned Index, const SCEV *Start, | |
} | ||
|
||
void RuntimePointerChecking::groupChecks( | ||
MemoryDepChecker::DepCandidates &DepCands, bool UseDependencies) { | ||
const MemoryDepChecker &DepChecker, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just checking if this pull request is still active. Also, it looks like we pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep will try and update it in the next few days, thanks |
||
MemoryDepChecker::DepCandidates &DepCands) { | ||
// We build the groups from dependency candidates equivalence classes | ||
// because: | ||
// - We know that pointers in the same equivalence class share | ||
|
@@ -495,21 +497,11 @@ void RuntimePointerChecking::groupChecks( | |
// us to perform an accurate check in this case. | ||
// | ||
// The above case requires that we have an UnknownDependence between | ||
// accesses to the same underlying object. This cannot happen unless | ||
// FoundNonConstantDistanceDependence is set, and therefore UseDependencies | ||
// is also false. In this case we will use the fallback path and create | ||
// separate checking groups for all pointers. | ||
|
||
// If we don't have the dependency partitions, construct a new | ||
// checking pointer group for each pointer. This is also required | ||
// for correctness, because in this case we can have checking between | ||
// pointers to the same underlying object. | ||
if (!UseDependencies) { | ||
for (unsigned I = 0; I < Pointers.size(); ++I) | ||
CheckingGroups.push_back(RuntimeCheckingPtrGroup(I, *this)); | ||
return; | ||
} | ||
|
||
// accesses to the same underlying object. It is the caller's responsibility | ||
// to clear any entries for accesses with unknown dependencies from the | ||
// dependency partition (DepCands). This is required for correctness, because | ||
// in this case we can have checking between pointers to the same underlying | ||
// object. | ||
unsigned TotalComparisons = 0; | ||
|
||
DenseMap<Value *, SmallVector<unsigned>> PositionMap; | ||
|
@@ -534,6 +526,13 @@ void RuntimePointerChecking::groupChecks( | |
MemoryDepChecker::MemAccessInfo Access(Pointers[I].PointerValue, | ||
Pointers[I].IsWritePtr); | ||
|
||
// If there is no entry in the dependency partition, there are no potential | ||
// accesses to merge; simply add a new pointer checking group. | ||
if (DepCands.findValue(Access) == DepCands.end()) { | ||
CheckingGroups.push_back(RuntimeCheckingPtrGroup(I, *this)); | ||
continue; | ||
} | ||
|
||
SmallVector<RuntimeCheckingPtrGroup, 2> Groups; | ||
auto LeaderI = DepCands.findValue(DepCands.getLeaderValue(Access)); | ||
|
||
|
@@ -702,8 +701,10 @@ class AccessAnalysis { | |
/// | ||
/// Returns true if we need no check or if we do and we can generate them | ||
/// (i.e. the pointers have computable bounds). | ||
bool canCheckPtrAtRT(RuntimePointerChecking &RtCheck, ScalarEvolution *SE, | ||
Loop *TheLoop, const DenseMap<Value *, const SCEV *> &Strides, | ||
bool canCheckPtrAtRT(const MemoryDepChecker &DepChecker, | ||
RuntimePointerChecking &RtCheck, ScalarEvolution *SE, | ||
Loop *TheLoop, | ||
const DenseMap<Value *, const SCEV *> &Strides, | ||
Comment on lines
+704
to
+707
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you document the (additional) parameters?
|
||
Value *&UncomputablePtr, bool ShouldCheckWrap = false); | ||
|
||
/// Goes over all memory accesses, checks whether a RT check is needed | ||
|
@@ -719,12 +720,6 @@ class AccessAnalysis { | |
/// dependency checking (i.e. FoundNonConstantDistanceDependence). | ||
bool isDependencyCheckNeeded() { return !CheckDeps.empty(); } | ||
|
||
/// We decided that no dependence analysis would be used. Reset the state. | ||
void resetDepChecks(MemoryDepChecker &DepChecker) { | ||
CheckDeps.clear(); | ||
DepChecker.clearDependences(); | ||
} | ||
|
||
MemAccessInfoList &getDependenciesToCheck() { return CheckDeps; } | ||
|
||
const DenseMap<Value *, SmallVector<const Value *, 16>> & | ||
|
@@ -1109,7 +1104,8 @@ bool AccessAnalysis::createCheckForAccess(RuntimePointerChecking &RtCheck, | |
// The id of the dependence set. | ||
unsigned DepId; | ||
|
||
if (isDependencyCheckNeeded()) { | ||
if (isDependencyCheckNeeded() && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style only: I think you're basically adding a variant of isDependencyChecksNeeded which is specific for a particular access, and having a well named cover function would clarify the code. |
||
DepCands.findValue(Access) != DepCands.end()) { | ||
Value *Leader = DepCands.getLeaderValue(Access).getPointer(); | ||
unsigned &LeaderId = DepSetId[Leader]; | ||
if (!LeaderId) | ||
|
@@ -1128,18 +1124,38 @@ bool AccessAnalysis::createCheckForAccess(RuntimePointerChecking &RtCheck, | |
return true; | ||
} | ||
|
||
bool AccessAnalysis::canCheckPtrAtRT(RuntimePointerChecking &RtCheck, | ||
ScalarEvolution *SE, Loop *TheLoop, | ||
const DenseMap<Value *, const SCEV *> &StridesMap, | ||
Value *&UncomputablePtr, bool ShouldCheckWrap) { | ||
bool AccessAnalysis::canCheckPtrAtRT( | ||
const MemoryDepChecker &DepChecker, RuntimePointerChecking &RtCheck, | ||
ScalarEvolution *SE, Loop *TheLoop, | ||
const DenseMap<Value *, const SCEV *> &StridesMap, Value *&UncomputablePtr, | ||
bool ShouldCheckWrap) { | ||
// Find pointers with computable bounds. We are going to use this information | ||
// to place a runtime bound check. | ||
bool CanDoRT = true; | ||
|
||
bool MayNeedRTCheck = false; | ||
if (!IsRTCheckAnalysisNeeded) return true; | ||
|
||
bool IsDepCheckNeeded = isDependencyCheckNeeded(); | ||
if (auto *Deps = DepChecker.getDependences()) { | ||
// If there are unknown dependences, this means runtime checks are needed to | ||
// ensure there's no overlap between accesses to the same underlying object. | ||
// Remove the equivalence classes containing both source and destination | ||
// accesses from DepCands. This ensures runtime checks will be generated | ||
// between those accesses and prevents them from being grouped together. | ||
for (const auto &Dep : *Deps) { | ||
if (Dep.Type != MemoryDepChecker::Dependence::Unknown) { | ||
assert(MemoryDepChecker::Dependence::isSafeForVectorization(Dep.Type) == | ||
MemoryDepChecker::VectorizationSafetyStatus::Safe && | ||
"Should only skip safe dependences"); | ||
continue; | ||
} | ||
Instruction *Src = Dep.getSource(DepChecker); | ||
Instruction *Dst = Dep.getDestination(DepChecker); | ||
DepCands.eraseClass({getPointerOperand(Src), Src->mayWriteToMemory()}); | ||
DepCands.eraseClass({getPointerOperand(Dst), Dst->mayWriteToMemory()}); | ||
} | ||
} else | ||
CheckDeps.clear(); | ||
Comment on lines
+1154
to
+1158
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [serious] Can we do without modifying state within a query function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for taking a look! At the moment, canCheckPtrAtRT both checks if runtime checks can be generated and then also generates them, populating various members. While modifying |
||
|
||
// We assign a consecutive id to access from different alias sets. | ||
// Accesses between different groups doesn't need to be checked. | ||
|
@@ -1265,7 +1281,7 @@ bool AccessAnalysis::canCheckPtrAtRT(RuntimePointerChecking &RtCheck, | |
} | ||
|
||
if (MayNeedRTCheck && CanDoRT) | ||
RtCheck.generateChecks(DepCands, IsDepCheckNeeded); | ||
RtCheck.generateChecks(DepChecker, DepCands); | ||
|
||
LLVM_DEBUG(dbgs() << "LAA: We need to do " << RtCheck.getNumberOfChecks() | ||
<< " pointer comparisons.\n"); | ||
|
@@ -2632,9 +2648,9 @@ bool LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, | |
// Find pointers with computable bounds. We are going to use this information | ||
// to place a runtime bound check. | ||
Value *UncomputablePtr = nullptr; | ||
bool CanDoRTIfNeeded = | ||
Accesses.canCheckPtrAtRT(*PtrRtChecking, PSE->getSE(), TheLoop, | ||
SymbolicStrides, UncomputablePtr, false); | ||
bool CanDoRTIfNeeded = Accesses.canCheckPtrAtRT( | ||
getDepChecker(), *PtrRtChecking, PSE->getSE(), TheLoop, SymbolicStrides, | ||
UncomputablePtr, false); | ||
if (!CanDoRTIfNeeded) { | ||
auto *I = dyn_cast_or_null<Instruction>(UncomputablePtr); | ||
recordAnalysis("CantIdentifyArrayBounds", I) | ||
|
@@ -2657,16 +2673,14 @@ bool LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, | |
if (!DepsAreSafe && DepChecker->shouldRetryWithRuntimeCheck()) { | ||
LLVM_DEBUG(dbgs() << "LAA: Retrying with memory checks\n"); | ||
|
||
// Clear the dependency checks. We assume they are not needed. | ||
Accesses.resetDepChecks(*DepChecker); | ||
|
||
PtrRtChecking->reset(); | ||
PtrRtChecking->Need = true; | ||
|
||
auto *SE = PSE->getSE(); | ||
UncomputablePtr = nullptr; | ||
CanDoRTIfNeeded = Accesses.canCheckPtrAtRT( | ||
*PtrRtChecking, SE, TheLoop, SymbolicStrides, UncomputablePtr, true); | ||
CanDoRTIfNeeded = | ||
Accesses.canCheckPtrAtRT(getDepChecker(), *PtrRtChecking, SE, TheLoop, | ||
SymbolicStrides, UncomputablePtr, true); | ||
|
||
// Check that we found the bounds for the pointer. | ||
if (!CanDoRTIfNeeded) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment could be clearer what "erasing" a class means. Reading the code, it removes all its members from the set.