aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-12-18 20:30:12 +0000
committerDimitry Andric <dim@FreeBSD.org>2024-04-19 21:12:03 +0000
commitc9157d925c489f07ba9c0b2ce47e5149b75969a5 (patch)
tree08bc4a3d9cad3f9ebffa558ddf140b9d9257b219 /contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
parent2a66844f606a35d68ad8a8061f4bea204274b3bc (diff)
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp154
1 files changed, 104 insertions, 50 deletions
diff --git a/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 34299f9dbb23..7c277518b21d 100644
--- a/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -110,6 +110,39 @@ using SCCNodeSet = SmallSetVector<Function *, 8>;
} // end anonymous namespace
+static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
+ ModRefInfo MR, AAResults &AAR) {
+ // Ignore accesses to known-invariant or local memory.
+ MR &= AAR.getModRefInfoMask(Loc, /*IgnoreLocal=*/true);
+ if (isNoModRef(MR))
+ return;
+
+ const Value *UO = getUnderlyingObject(Loc.Ptr);
+ assert(!isa<AllocaInst>(UO) &&
+ "Should have been handled by getModRefInfoMask()");
+ if (isa<Argument>(UO)) {
+ ME |= MemoryEffects::argMemOnly(MR);
+ return;
+ }
+
+ // If it's not an identified object, it might be an argument.
+ if (!isIdentifiedObject(UO))
+ ME |= MemoryEffects::argMemOnly(MR);
+ ME |= MemoryEffects(IRMemLocation::Other, MR);
+}
+
+static void addArgLocs(MemoryEffects &ME, const CallBase *Call,
+ ModRefInfo ArgMR, AAResults &AAR) {
+ for (const Value *Arg : Call->args()) {
+ if (!Arg->getType()->isPtrOrPtrVectorTy())
+ continue;
+
+ addLocAccess(ME,
+ MemoryLocation::getBeforeOrAfter(Arg, Call->getAAMetadata()),
+ ArgMR, AAR);
+ }
+}
+
/// Returns the memory access attribute for function F using AAR for AA results,
/// where SCCNodes is the current SCC.
///
@@ -118,54 +151,48 @@ using SCCNodeSet = SmallSetVector<Function *, 8>;
/// result will be based only on AA results for the function declaration; it
/// will be assumed that some other (perhaps less optimized) version of the
/// function may be selected at link time.
-static MemoryEffects checkFunctionMemoryAccess(Function &F, bool ThisBody,
- AAResults &AAR,
- const SCCNodeSet &SCCNodes) {
+///
+/// The return value is split into two parts: Memory effects that always apply,
+/// and additional memory effects that apply if any of the functions in the SCC
+/// can access argmem.
+static std::pair<MemoryEffects, MemoryEffects>
+checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
+ const SCCNodeSet &SCCNodes) {
MemoryEffects OrigME = AAR.getMemoryEffects(&F);
if (OrigME.doesNotAccessMemory())
// Already perfect!
- return OrigME;
+ return {OrigME, MemoryEffects::none()};
if (!ThisBody)
- return OrigME;
+ return {OrigME, MemoryEffects::none()};
MemoryEffects ME = MemoryEffects::none();
+ // Additional locations accessed if the SCC accesses argmem.
+ MemoryEffects RecursiveArgME = MemoryEffects::none();
+
// Inalloca and preallocated arguments are always clobbered by the call.
if (F.getAttributes().hasAttrSomewhere(Attribute::InAlloca) ||
F.getAttributes().hasAttrSomewhere(Attribute::Preallocated))
ME |= MemoryEffects::argMemOnly(ModRefInfo::ModRef);
- auto AddLocAccess = [&](const MemoryLocation &Loc, ModRefInfo MR) {
- // Ignore accesses to known-invariant or local memory.
- MR &= AAR.getModRefInfoMask(Loc, /*IgnoreLocal=*/true);
- if (isNoModRef(MR))
- return;
-
- const Value *UO = getUnderlyingObject(Loc.Ptr);
- assert(!isa<AllocaInst>(UO) &&
- "Should have been handled by getModRefInfoMask()");
- if (isa<Argument>(UO)) {
- ME |= MemoryEffects::argMemOnly(MR);
- return;
- }
-
- // If it's not an identified object, it might be an argument.
- if (!isIdentifiedObject(UO))
- ME |= MemoryEffects::argMemOnly(MR);
- ME |= MemoryEffects(IRMemLocation::Other, MR);
- };
// Scan the function body for instructions that may read or write memory.
for (Instruction &I : instructions(F)) {
// Some instructions can be ignored even if they read or write memory.
// Detect these now, skipping to the next instruction if one is found.
if (auto *Call = dyn_cast<CallBase>(&I)) {
- // Ignore calls to functions in the same SCC, as long as the call sites
- // don't have operand bundles. Calls with operand bundles are allowed to
- // have memory effects not described by the memory effects of the call
- // target.
+ // We can optimistically ignore calls to functions in the same SCC, with
+ // two caveats:
+ // * Calls with operand bundles may have additional effects.
+ // * Argument memory accesses may imply additional effects depending on
+ // what the argument location is.
if (!Call->hasOperandBundles() && Call->getCalledFunction() &&
- SCCNodes.count(Call->getCalledFunction()))
+ SCCNodes.count(Call->getCalledFunction())) {
+ // Keep track of which additional locations are accessed if the SCC
+ // turns out to access argmem.
+ addArgLocs(RecursiveArgME, Call, ModRefInfo::ModRef, AAR);
continue;
+ }
+
MemoryEffects CallME = AAR.getMemoryEffects(Call);
// If the call doesn't access memory, we're done.
@@ -190,15 +217,8 @@ static MemoryEffects checkFunctionMemoryAccess(Function &F, bool ThisBody,
// Check whether all pointer arguments point to local memory, and
// ignore calls that only access local memory.
ModRefInfo ArgMR = CallME.getModRef(IRMemLocation::ArgMem);
- if (ArgMR != ModRefInfo::NoModRef) {
- for (const Use &U : Call->args()) {
- const Value *Arg = U;
- if (!Arg->getType()->isPtrOrPtrVectorTy())
- continue;
-
- AddLocAccess(MemoryLocation::getBeforeOrAfter(Arg, I.getAAMetadata()), ArgMR);
- }
- }
+ if (ArgMR != ModRefInfo::NoModRef)
+ addArgLocs(ME, Call, ArgMR, AAR);
continue;
}
@@ -222,15 +242,15 @@ static MemoryEffects checkFunctionMemoryAccess(Function &F, bool ThisBody,
if (I.isVolatile())
ME |= MemoryEffects::inaccessibleMemOnly(MR);
- AddLocAccess(*Loc, MR);
+ addLocAccess(ME, *Loc, MR, AAR);
}
- return OrigME & ME;
+ return {OrigME & ME, RecursiveArgME};
}
MemoryEffects llvm::computeFunctionBodyMemoryAccess(Function &F,
AAResults &AAR) {
- return checkFunctionMemoryAccess(F, /*ThisBody=*/true, AAR, {});
+ return checkFunctionMemoryAccess(F, /*ThisBody=*/true, AAR, {}).first;
}
/// Deduce readonly/readnone/writeonly attributes for the SCC.
@@ -238,24 +258,37 @@ template <typename AARGetterT>
static void addMemoryAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter,
SmallSet<Function *, 8> &Changed) {
MemoryEffects ME = MemoryEffects::none();
+ MemoryEffects RecursiveArgME = MemoryEffects::none();
for (Function *F : SCCNodes) {
// Call the callable parameter to look up AA results for this function.
AAResults &AAR = AARGetter(*F);
// Non-exact function definitions may not be selected at link time, and an
// alternative version that writes to memory may be selected. See the
// comment on GlobalValue::isDefinitionExact for more details.
- ME |= checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, SCCNodes);
+ auto [FnME, FnRecursiveArgME] =
+ checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, SCCNodes);
+ ME |= FnME;
+ RecursiveArgME |= FnRecursiveArgME;
// Reached bottom of the lattice, we will not be able to improve the result.
if (ME == MemoryEffects::unknown())
return;
}
+ // If the SCC accesses argmem, add recursive accesses resulting from that.
+ ModRefInfo ArgMR = ME.getModRef(IRMemLocation::ArgMem);
+ if (ArgMR != ModRefInfo::NoModRef)
+ ME |= RecursiveArgME & MemoryEffects(ArgMR);
+
for (Function *F : SCCNodes) {
MemoryEffects OldME = F->getMemoryEffects();
MemoryEffects NewME = ME & OldME;
if (NewME != OldME) {
++NumMemoryAttr;
F->setMemoryEffects(NewME);
+ // Remove conflicting writable attributes.
+ if (!isModSet(NewME.getModRef(IRMemLocation::ArgMem)))
+ for (Argument &A : F->args())
+ A.removeAttr(Attribute::Writable);
Changed.insert(F);
}
}
@@ -625,7 +658,15 @@ determinePointerAccessAttrs(Argument *A,
// must be a data operand (e.g. argument or operand bundle)
const unsigned UseIndex = CB.getDataOperandNo(U);
- if (!CB.doesNotCapture(UseIndex)) {
+ // Some intrinsics (for instance ptrmask) do not capture their results,
+ // but return results thas alias their pointer argument, and thus should
+ // be handled like GEP or addrspacecast above.
+ if (isIntrinsicReturningPointerAliasingArgumentWithoutCapturing(
+ &CB, /*MustPreserveNullness=*/false)) {
+ for (Use &UU : CB.uses())
+ if (Visited.insert(&UU).second)
+ Worklist.push_back(&UU);
+ } else if (!CB.doesNotCapture(UseIndex)) {
if (!CB.onlyReadsMemory())
// If the callee can save a copy into other memory, then simply
// scanning uses of the call is insufficient. We have no way
@@ -639,7 +680,8 @@ determinePointerAccessAttrs(Argument *A,
Worklist.push_back(&UU);
}
- if (CB.doesNotAccessMemory())
+ ModRefInfo ArgMR = CB.getMemoryEffects().getModRef(IRMemLocation::ArgMem);
+ if (isNoModRef(ArgMR))
continue;
if (Function *F = CB.getCalledFunction())
@@ -654,9 +696,9 @@ determinePointerAccessAttrs(Argument *A,
// invokes with operand bundles.
if (CB.doesNotAccessMemory(UseIndex)) {
/* nop */
- } else if (CB.onlyReadsMemory() || CB.onlyReadsMemory(UseIndex)) {
+ } else if (!isModSet(ArgMR) || CB.onlyReadsMemory(UseIndex)) {
IsRead = true;
- } else if (CB.hasFnAttr(Attribute::WriteOnly) ||
+ } else if (!isRefSet(ArgMR) ||
CB.dataOperandHasImpliedAttr(UseIndex, Attribute::WriteOnly)) {
IsWrite = true;
} else {
@@ -810,6 +852,9 @@ static bool addAccessAttr(Argument *A, Attribute::AttrKind R) {
A->removeAttr(Attribute::WriteOnly);
A->removeAttr(Attribute::ReadOnly);
A->removeAttr(Attribute::ReadNone);
+ // Remove conflicting writable attribute.
+ if (R == Attribute::ReadNone || R == Attribute::ReadOnly)
+ A->removeAttr(Attribute::Writable);
A->addAttr(R);
if (R == Attribute::ReadOnly)
++NumReadOnlyArg;
@@ -1720,7 +1765,8 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
template <typename AARGetterT>
static SmallSet<Function *, 8>
-deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter) {
+deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
+ bool ArgAttrsOnly) {
SCCNodesResult Nodes = createSCCNodeSet(Functions);
// Bail if the SCC only contains optnone functions.
@@ -1728,6 +1774,10 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter) {
return {};
SmallSet<Function *, 8> Changed;
+ if (ArgAttrsOnly) {
+ addArgumentAttrs(Nodes.SCCNodes, Changed);
+ return Changed;
+ }
addArgumentReturnedAttrs(Nodes.SCCNodes, Changed);
addMemoryAttrs(Nodes.SCCNodes, AARGetter, Changed);
@@ -1762,10 +1812,13 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
LazyCallGraph &CG,
CGSCCUpdateResult &) {
// Skip non-recursive functions if requested.
+ // Only infer argument attributes for non-recursive functions, because
+ // it can affect optimization behavior in conjunction with noalias.
+ bool ArgAttrsOnly = false;
if (C.size() == 1 && SkipNonRecursive) {
LazyCallGraph::Node &N = *C.begin();
if (!N->lookup(N))
- return PreservedAnalyses::all();
+ ArgAttrsOnly = true;
}
FunctionAnalysisManager &FAM =
@@ -1782,7 +1835,8 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
Functions.push_back(&N.getFunction());
}
- auto ChangedFunctions = deriveAttrsInPostOrder(Functions, AARGetter);
+ auto ChangedFunctions =
+ deriveAttrsInPostOrder(Functions, AARGetter, ArgAttrsOnly);
if (ChangedFunctions.empty())
return PreservedAnalyses::all();
@@ -1818,7 +1872,7 @@ void PostOrderFunctionAttrsPass::printPipeline(
static_cast<PassInfoMixin<PostOrderFunctionAttrsPass> *>(this)->printPipeline(
OS, MapClassName2PassName);
if (SkipNonRecursive)
- OS << "<skip-non-recursive>";
+ OS << "<skip-non-recursive-function-attrs>";
}
template <typename AARGetterT>