[llvm] Perf/goldsteinn/nocapture wip (PR #113116)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 20 18:02:08 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: None (goldsteinn)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/113116.diff
1 Files Affected:
- (modified) llvm/lib/Transforms/Utils/InlineFunction.cpp (+124-1)
``````````diff
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 4ad426285ce2f0..a3c7337262313a 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -1351,6 +1351,114 @@ static bool MayContainThrowingOrExitingCallAfterCB(CallBase *Begin,
++BeginIt, End->getIterator(), InlinerAttributeWindow + 1);
}
+template <typename RangeT> static bool ContainsSideEffects(RangeT Range) {
+ // Any instruction that may clear local scratch space CB stored
+ // into.
+
+ return any_of(Range, [](Instruction &I) { return I.mayHaveSideEffects(); });
+}
+
+template <typename RangeT> static bool ContainsScratchSpace(RangeT Range) {
+ return any_of(Range, [](Instruction &I) {
+ // Any instruction that may create local scratch space CB can store
+ // into.
+ return I.mayHaveSideEffects() || isa<AllocaInst>(&I);
+ });
+}
+
+static bool CheckPathFromBB(DenseMap<BasicBlock *, bool> &CachedRes,
+ BasicBlock *BB, bool From) {
+ // Initialize to true (okay to propagate) `nocapture`. This means that loops
+ // will be okay.
+ auto [Iter, Inserted] = CachedRes.try_emplace(BB, true);
+ // If we already have a result, return it.
+ if (!Inserted)
+ return Iter->second;
+
+ if (From) {
+ // Continue checking successors
+ if (ContainsSideEffects(BB->instructionsWithoutDebug())) {
+ CachedRes[BB] = false;
+ return false;
+ }
+ for (BasicBlock *Succ : successors(BB)) {
+ if (!CheckPathFromBB(CachedRes, Succ, From)) {
+ CachedRes[BB] = false;
+ return false;
+ }
+ }
+ } else {
+ // Continue checking preds
+ if (ContainsScratchSpace(BB->instructionsWithoutDebug())) {
+ CachedRes[BB] = false;
+ return false;
+ }
+ for (BasicBlock *Pred : predecessors(BB)) {
+ if (!CheckPathFromBB(CachedRes, Pred, From)) {
+ CachedRes[BB] = false;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Assuming we have:
+// define @foo(ptr nocapture %p) {
+// entry:
+// ...
+// bar (ptr %p)
+// ...
+// }
+//
+// Determine if we can propagate `nocapture` to the `%p` at the
+// `bar`.
+// First if `bar` is non-void, we only propagate if the only users of
+// `bar` are return instructions from `foo` (meaning if `bar` captured
+// via return, `foo` would too).
+// Next We do an extremely conservatively check and only propagate
+// the `nocapture` if the `bar` has no side-effects or if `bar` is the only
+// instruction with side-effects on all paths from `entry` that go through the
+// `bar` and there are no `alloca` instructions. This accomplishes two things.
+// 1) It ensures that after ``bar``, there is no way a store/other could "clean
+// up" an captures from `bar`. 2) There is no local state (i.e `alloca` or a
+// local `malloc`) that could `bar` could have stored `p` in.
+static bool
+CanPropagateNoCaptureAtCB(DenseMap<BasicBlock *, bool> &PureFromBB,
+ DenseMap<BasicBlock *, bool> &NoLocalStateToBB,
+ BasicBlock *BB, CallBase *CB) {
+ // Check that we can't capture via return.
+ if (!CB->getType()->isVoidTy())
+ for (auto Use : CB->users())
+ if (!isa<ReturnInst>(Use))
+ return false;
+
+ // Can't capture via return, so if no side-effects we are set.
+ // if (!CB->mayHaveSideEffects())
+ // return true;
+
+ auto It = CB->getIterator();
+ ++It;
+
+ if (ContainsSideEffects(make_range(It, BB->end())) ||
+ ContainsScratchSpace(make_range(BB->begin(), CB->getIterator())))
+ return false;
+
+ for (BasicBlock *Succ : successors(BB)) {
+ if (!CheckPathFromBB(PureFromBB, Succ, /*From=*/true)) {
+ PureFromBB[Succ] = false;
+ return false;
+ }
+ }
+ for (BasicBlock *Pred : predecessors(BB)) {
+ if (!CheckPathFromBB(NoLocalStateToBB, Pred, /*From=*/false)) {
+ NoLocalStateToBB[Pred] = false;
+ return false;
+ }
+ }
+ return true;
+}
+
// Add attributes from CB params and Fn attributes that can always be propagated
// to the corresponding argument / inner callbases.
static void AddParamAndFnBasicAttributes(const CallBase &CB,
@@ -1363,6 +1471,9 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
SmallVector<AttrBuilder> ValidObjParamAttrs, ValidExactParamAttrs;
bool HasAttrToPropagate = false;
+ DenseMap<BasicBlock *, bool> PureFromBB{};
+ DenseMap<BasicBlock *, bool> NoLocalStateToBB{};
+
// Attributes we can only propagate if the exact parameter is forwarded.
// We can propagate both poison generating and UB generating attributes
// without any extra checks. The only attribute that is tricky to propagate
@@ -1381,6 +1492,8 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
ValidObjParamAttrs.back().addAttribute(Attribute::ReadNone);
if (CB.paramHasAttr(I, Attribute::ReadOnly))
ValidObjParamAttrs.back().addAttribute(Attribute::ReadOnly);
+ if (CB.paramHasAttr(I, Attribute::NoCapture))
+ ValidObjParamAttrs.back().addAttribute(Attribute::NoCapture);
for (Attribute::AttrKind AK : ExactAttrsToPropagate) {
Attribute Attr = CB.getParamAttr(I, AK);
@@ -1463,8 +1576,18 @@ static void AddParamAndFnBasicAttributes(const CallBase &CB,
ArgNo = Arg->getArgNo();
}
+ AttributeSet AS = AttributeSet::get(Context, ValidObjParamAttrs[ArgNo]);
+ // Check if we can propagate `nocapture`.
+ if (AS.hasAttribute(Attribute::NoCapture)) {
+ if (NewInnerCB->paramHasAttr(I, Attribute::NoCapture))
+ AS = AS.removeAttribute(Context, Attribute::NoCapture);
+ else if (!CanPropagateNoCaptureAtCB(PureFromBB, NoLocalStateToBB, &BB,
+ cast<CallBase>(&Ins)))
+ AS = AS.removeAttribute(Context, Attribute::NoCapture);
+ }
+
// If so, propagate its access attributes.
- AL = AL.addParamAttributes(Context, I, ValidObjParamAttrs[ArgNo]);
+ AL = AL.addParamAttributes(Context, I, AttrBuilder{Context, AS});
// We can have conflicting attributes from the inner callsite and
// to-be-inlined callsite. In that case, choose the most
// restrictive.
``````````
</details>
https://github.com/llvm/llvm-project/pull/113116
More information about the llvm-commits
mailing list