[Attributor][NFC] Performance improvements (#122923)

` forallInterferingAccesses` is a hotspot and for large modules these
changes make a measurable improvement in compilation time.

For LTO kernel compilation of 519.clvleaf (SPEChpc 2021) I measured the
following:
```
                    |   Measured times (s)   | Average | speedup
--------------------+------------------------+---------+---------
Baseline            | 33.268  33.332  33.275 |  33.292 |      0%
Cache "kernel"      | 30.543  30.339  30.607 |  30.496 |    9.2%
templatize callback | 30.981  30.97   30.964 |  30.972 |    7.5%
Both changes        | 29.284  29.201  29.053 |  29.179 |   14.1%
```
This commit is contained in:
macurtis-amd
2025-01-14 12:51:25 -06:00
committed by GitHub
parent 63d3bd6d0c
commit d1a6eaa478
3 changed files with 23 additions and 14 deletions

View File

@@ -1287,6 +1287,12 @@ struct InformationCache {
return AG.getAnalysis<TargetLibraryAnalysis>(F);
}
/// Return true if \p F has the "kernel" function attribute
bool isKernel(const Function &F) {
FunctionInfo &FI = getFunctionInfo(F);
return FI.IsKernel;
}
/// Return true if \p Arg is involved in a must-tail call, thus the argument
/// of the caller or callee.
bool isInvolvedInMustTailCall(const Argument &Arg) {
@@ -1361,6 +1367,9 @@ private:
/// Function contains a `musttail` call.
bool ContainsMustTailCall;
/// Function has the `"kernel"` attribute
bool IsKernel;
};
/// A map type from functions to informatio about it.

View File

@@ -678,8 +678,8 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI,
// kernel, values like allocas and shared memory are not accessible. We
// implicitly check for this situation to avoid costly lookups.
if (GoBackwardsCB && &ToFn != FromI.getFunction() &&
!GoBackwardsCB(*FromI.getFunction()) && ToFn.hasFnAttribute("kernel") &&
FromI.getFunction()->hasFnAttribute("kernel")) {
!GoBackwardsCB(*FromI.getFunction()) && A.getInfoCache().isKernel(ToFn) &&
A.getInfoCache().isKernel(*FromI.getFunction())) {
LLVM_DEBUG(dbgs() << "[AA] assume kernel cannot be reached from within the "
"module; success\n";);
return false;
@@ -3191,6 +3191,8 @@ void InformationCache::initializeInformationCache(const Function &CF,
// initialize the cache eagerly which would look the same to the users.
Function &F = const_cast<Function &>(CF);
FI.IsKernel = F.hasFnAttribute("kernel");
// Walk all instructions to find interesting instructions that might be
// queried by abstract attributes during their initialization or update.
// This has to happen before we create attributes.

View File

@@ -885,9 +885,8 @@ protected:
AAPointerInfo::OffsetInfo ReturnedOffsets;
/// See AAPointerInfo::forallInterferingAccesses.
bool forallInterferingAccesses(
AA::RangeTy Range,
function_ref<bool(const AAPointerInfo::Access &, bool)> CB) const {
template <typename F>
bool forallInterferingAccesses(AA::RangeTy Range, F CB) const {
if (!isValidState() || !ReturnedOffsets.isUnassigned())
return false;
@@ -906,10 +905,9 @@ protected:
}
/// See AAPointerInfo::forallInterferingAccesses.
bool forallInterferingAccesses(
Instruction &I,
function_ref<bool(const AAPointerInfo::Access &, bool)> CB,
AA::RangeTy &Range) const {
template <typename F>
bool forallInterferingAccesses(Instruction &I, F CB,
AA::RangeTy &Range) const {
if (!isValidState() || !ReturnedOffsets.isUnassigned())
return false;
@@ -1176,7 +1174,7 @@ struct AAPointerInfoImpl
// TODO: Use reaching kernels from AAKernelInfo (or move it to
// AAExecutionDomain) such that we allow scopes other than kernels as long
// as the reaching kernels are disjoint.
bool InstInKernel = Scope.hasFnAttribute("kernel");
bool InstInKernel = A.getInfoCache().isKernel(Scope);
bool ObjHasKernelLifetime = false;
const bool UseDominanceReasoning =
FindInterferingWrites && IsKnownNoRecurse;
@@ -1210,7 +1208,7 @@ struct AAPointerInfoImpl
// If the alloca containing function is not recursive the alloca
// must be dead in the callee.
const Function *AIFn = AI->getFunction();
ObjHasKernelLifetime = AIFn->hasFnAttribute("kernel");
ObjHasKernelLifetime = A.getInfoCache().isKernel(*AIFn);
bool IsKnownNoRecurse;
if (AA::hasAssumedIRAttr<Attribute::NoRecurse>(
A, this, IRPosition::function(*AIFn), DepClassTy::OPTIONAL,
@@ -1222,8 +1220,8 @@ struct AAPointerInfoImpl
// as it is "dead" in the (unknown) callees.
ObjHasKernelLifetime = HasKernelLifetime(GV, *GV->getParent());
if (ObjHasKernelLifetime)
IsLiveInCalleeCB = [](const Function &Fn) {
return !Fn.hasFnAttribute("kernel");
IsLiveInCalleeCB = [&A](const Function &Fn) {
return !A.getInfoCache().isKernel(Fn);
};
}
@@ -1238,7 +1236,7 @@ struct AAPointerInfoImpl
// If the object has kernel lifetime we can ignore accesses only reachable
// by other kernels. For now we only skip accesses *in* other kernels.
if (InstInKernel && ObjHasKernelLifetime && !AccInSameScope &&
AccScope->hasFnAttribute("kernel"))
A.getInfoCache().isKernel(*AccScope))
return true;
if (Exact && Acc.isMustAccess() && Acc.getRemoteInst() != &I) {