-
Notifications
You must be signed in to change notification settings - Fork 11k
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
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Vedant Paranjape (vedantparanjape-amd) ChangesTries to fix llvm#1259. This implementation fails pr42028-recursion.ll, the compiler crashes as it is not able to handle call graph as follows:
Full diff: https://github.com/llvm/llvm-project/pull/78735.diff 1 Files Affected:
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)) {
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
013ead7
to
9a6065d
Compare
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.
I haven't looked at this code in a long time. Left some comments though.
Tests missing.
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.
Reverse ping
@arsenm @jdoerfert I have addressed the comment, but I am stuck at one thing. The testcase: Transforms/ArgumentPromotion/pr42028-recursion.ll
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. |
This PR hasn't been updated? |
Just a min, cleaning up and pushing. |
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. |
@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. |
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 Value Tracking in recursive calls with SCC size greater than 1, we bail out in such cases.
2644a23
to
5a053f9
Compare
@@ -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()) { |
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.
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.
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.
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.
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 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
}
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 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.
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.
created a new PR to address these changes, please take a look (#98657)
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.
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
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