Skip to content
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

[ArgPromotion] Handle pointer arguments of recursive calls #78735

Merged
merged 19 commits into from
Jul 12, 2024

Conversation

vedantparanjape-amd
Copy link
Member

@vedantparanjape-amd vedantparanjape-amd commented Jan 19, 2024

Argument promotion doesn't handle recursive function calls to promote arguments. This patch adds functionality to handle self recursive function calls, i.e. whose SCC size is 1. Due to complexity of ValueTracking in recursive calls with SCC size greater than 1, we bail out in such cases

Fixes #1259

@llvmbot
Copy link
Collaborator

llvmbot commented Jan 19, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Vedant Paranjape (vedantparanjape-amd)

Changes

Tries to fix llvm#1259. This implementation fails pr42028-recursion.ll, the compiler crashes as it is not able to handle call graph as follows:

A -> B -> C
         ↑____|

Full diff: https://github.com/llvm/llvm-project/pull/78735.diff

1 Files Affected:

  • (modified) llvm/lib/Transforms/IPO/ArgumentPromotion.cpp (+54-1)
diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
index 8058282c422503..072a8c46f1e54c 100644
--- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -609,13 +609,66 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
       // unknown users
     }
 
+    auto *CI = dyn_cast<CallInst>(V);
+    if (IsRecursive && CI && CI->getFunction() == Arg->getParent()) {
+      dbgs() << "Found recursive call\n";
+      Type *Ty = CI->getType();
+      Value *Ptr = CI->getArgOperand(U->getOperandNo());
+      Align PtrAlign = Ptr->getPointerAlignment(DL);
+      APInt Offset(DL.getIndexTypeSizeInBits(Ptr->getType()), 0);
+      Ptr = Ptr->stripAndAccumulateConstantOffsets(DL, Offset, /* AllowNonInbounds */ true);
+      if (Ptr != Arg)
+        return false;
+
+      if (Offset.getSignificantBits() >= 64)
+        return false;
+
+      TypeSize Size = DL.getTypeStoreSize(Ty);
+      // Don't try to promote scalable types.
+      if (Size.isScalable())
+        return false;
+
+      int64_t Off = Offset.getSExtValue();
+      auto Pair = ArgParts.try_emplace(
+          Off, ArgPart{Ty, PtrAlign, nullptr});
+      ArgPart &Part = Pair.first->second;
+
+      // We limit promotion to only promoting up to a fixed number of elements of
+      // the aggregate.
+      if (MaxElements > 0 && ArgParts.size() > MaxElements) {
+        LLVM_DEBUG(dbgs() << "ArgPromotion of " << *Arg << " failed: "
+                          << "more than " << MaxElements << " parts\n");
+        return false;
+      }
+
+      Part.Alignment = std::max(Part.Alignment, PtrAlign);
+      continue;
+    }
     // Unknown user.
     LLVM_DEBUG(dbgs() << "ArgPromotion of " << *Arg << " failed: "
                       << "unknown user " << *V << "\n");
     return false;
   }
 
-  if (NeededDerefBytes || NeededAlign > 1) {
+  // Incase of functions with recursive calls, this check will fail when it
+  // tries to look at the first caller of this function. The caller may or may
+  // not have a load, incase it doesn't load the pointer being passed, this
+  // check will fail. So, it's safe to skip the check incase we know that we
+  // are dealing with a recursive call.
+  //
+  // def fun(ptr %a) {
+  //   ...
+  //   %loadres = load i32, ptr %a, align 4
+  //   %res = call i32 @fun(ptr %a)
+  //   ...
+  // }
+  //
+  // def bar(ptr %x) {
+  //   ...
+  //   %resbar = call i32 @fun(ptr %x)
+  //   ...
+  // }
+  if (!IsRecursive && (NeededDerefBytes || NeededAlign > 1)) {
     // Try to prove a required deref / aligned requirement.
     if (!allCallersPassValidPointerForArgument(Arg, NeededAlign,
                                                NeededDerefBytes)) {

Copy link

github-actions bot commented Jan 19, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@jdoerfert jdoerfert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at this code in a long time. Left some comments though.

Tests missing.

llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverse ping

@vedantparanjape-amd
Copy link
Member Author

vedantparanjape-amd commented May 31, 2024

@arsenm @jdoerfert I have addressed the comment, but I am stuck at one thing. The testcase: Transforms/ArgumentPromotion/pr42028-recursion.ll

A -> B -> C
     ↑____|

It has a SCC like this. Somehow, not able to handle the case with call instructions.

@arsenm
Copy link
Contributor

arsenm commented May 31, 2024

@arsenm @jdoerfert I have addressed the comment, but I am stuck at one thing. The testcase: Transforms/ArgumentPromotion/pr42028-recursion.ll

A -> B -> C
     ↑____|

It has a SCC like this. Somehow, not able to handle the case with call instructions.

Handle in what way? It breaks, or is sub-optimal? If it's not broken, it doesn't need to be perfect in the first attempt

@vedantparanjape-amd
Copy link
Member Author

vedantparanjape-amd commented May 31, 2024

@arsenm @jdoerfert I have addressed the comment, but I am stuck at one thing. The testcase: Transforms/ArgumentPromotion/pr42028-recursion.ll

A -> B -> C
     ↑____|

It has a SCC like this. Somehow, not able to handle the case with call instructions.

Handle in what way? It breaks, or is sub-optimal? If it's not broken, it doesn't need to be perfect in the first attempt

It breaks (SE), while handling that case, if we try to do argument promotion for such a case it will lead to recursion. Anyways when we try to handle such a case, it asserts at isAllocaPromotable() check, because we cannot promote Allocas which are a ptr, as we don't know the depth of pointer nesting.

@arsenm
Copy link
Contributor

arsenm commented May 31, 2024

@arsenm @jdoerfert I have addressed the comment,

This PR hasn't been updated?

@vedantparanjape-amd
Copy link
Member Author

@arsenm @jdoerfert I have addressed the comment,

This PR hasn't been updated?

Just a min, cleaning up and pushing.

@vedantparanjape-amd
Copy link
Member Author

@arsenm @jdoerfert I have addressed the comment,

This PR hasn't been updated?

Done, please take a look! adding the testcases in sometime.

@arsenm
Copy link
Contributor

arsenm commented May 31, 2024

Done, please take a look! adding the testcases in sometime.

The test is the most important part of the review

@vedantparanjape-amd
Copy link
Member Author

Done, please take a look! adding the testcases in sometime.

The test is the most important part of the review

I have added the testcase as well, and addressed the comments best I could.

@vedantparanjape-amd
Copy link
Member Author

@arsenm implemented a check on function call args to see if they are being dereferenced somewhere above the call. If they are not we can bail out, and not promote the argument. This will remove the need for IsSelfRecursive flag, and provide a better way to detect recursive arg promotions.

@arsenm arsenm added the ipo Interprocedural optimizations label Jun 6, 2024
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Show resolved Hide resolved
llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Show resolved Hide resolved
@@ -611,6 +639,33 @@ static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR,
// unknown users
}

auto *CB = dyn_cast<CallBase>(V);
Value *PtrArg = cast<Value>(U);
if (CB && PtrArg && CB->getCalledFunction() == CB->getFunction()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably also check that the function type of the call and function match? I wouldn't want to reason about how the arguments map if this is not the case.

Copy link
Member Author

@vedantparanjape-amd vedantparanjape-amd Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by function type, is it the thing returned by getFunctionType() ? I am not able to understand why it would be different ? Can you give an example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call and the function have separate types that do not match:

declare float @ret_float()

define i32 @call_as_i32() {
  %val = call i32 @ret_float()
  ret i32 %val
}

Copy link
Member Author

@vedantparanjape-amd vedantparanjape-amd Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call and the function have separate types that do not match:

declare float @ret_float()

define i32 @call_as_i32() {
  %val = call i32 @ret_float()
  ret i32 %val
}

I didn't know this was possible, I mean it should not be valid? Okay, I will check for the return types as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created a new PR to address these changes, please take a look (#98657)

llvm/lib/Transforms/IPO/ArgumentPromotion.cpp Show resolved Hide resolved
vedantparanjape-amd added a commit that referenced this pull request Jul 14, 2024
#98657)

This patch further cleans up the implementation by removing some
redundant checks and replacing cast<> with get() calls.

This contribution is based on the discussion in #78735
aaryanshukla pushed a commit to aaryanshukla/llvm-project that referenced this pull request Jul 14, 2024
Argument promotion doesn't handle recursive function calls to promote
arguments. This patch adds functionality to handle self recursive
function calls, i.e. whose SCC size is 1. Due to complexity of
ValueTracking in recursive calls with SCC size greater than 1, we bail
out in such cases.
aaryanshukla pushed a commit to aaryanshukla/llvm-project that referenced this pull request Jul 14, 2024
aaryanshukla pushed a commit to aaryanshukla/llvm-project that referenced this pull request Jul 14, 2024
llvm#98657)

This patch further cleans up the implementation by removing some
redundant checks and replacing cast<> with get() calls.

This contribution is based on the discussion in llvm#78735
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ipo Interprocedural optimizations llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Argument Promotion needs to be smarter
6 participants