-
Notifications
You must be signed in to change notification settings - Fork 11.2k
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
[SCEV] Support addrec in right hand side in howManyLessThans #92560
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write If you have received no comments on your PR for a week, you can request a review If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-llvm-analysis Author: vaibhav (mrdaybird) ChangesFixes #92554 (std::reverse will auto-vectorize now) When calculating number of times a exit condition containing a comparison is executed, we mostly assume that RHS of comparison should be loop invariant, but it may be another add-recurrence. In that case, we can try the computation with For now, I have only changed Full diff: https://github.com/llvm/llvm-project/pull/92560.diff 1 Files Affected:
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 515b9d0744f6e..9bf5bf80b4570 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12941,12 +12941,16 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
return RHS;
}
- // When the RHS is not invariant, we do not know the end bound of the loop and
- // cannot calculate the ExactBECount needed by ExitLimit. However, we can
- // calculate the MaxBECount, given the start, stride and max value for the end
- // bound of the loop (RHS), and the fact that IV does not overflow (which is
- // checked above).
if (!isLoopInvariant(RHS, L)) {
+ // If RHS is an add recurrence, try again with lhs=lhs-rhs and rhs=0
+ if(auto RHSAddRec = dyn_cast<SCEVAddRecExpr>(RHS)){
+ return howManyLessThans(getMinusSCEV(IV, RHSAddRec),
+ getZero(IV->getType()), L, true, ControlsOnlyExit, AllowPredicates);
+ }
+ // If we cannot calculate ExactBECount, we can calculate the MaxBECount,
+ // given the start, stride and max value for the end bound of the
+ // loop (RHS), and the fact that IV does not overflow (which is
+ // checked above).
const SCEV *MaxBECount = computeMaxBECountForLT(
Start, Stride, RHS, getTypeSizeInBits(LHS->getType()), IsSigned);
return ExitLimit(getCouldNotCompute() /* ExactNotTaken */, MaxBECount,
|
I had a few queries:
|
They probably should be merged... but it's not trivial. If you just blindly NOT both sides, the result would be correct, but probably less accurate. Otherwise, you need to add a bunch of conditionals to track whether you're supposed to be incrementing or decrementing.
In general, if the RHS is loop-variant, the LHS can potentially wrap even if the loop isn't infinite. I don't think restricting to add-recurrences on the RHS changes that. |
@efriedma-quic Thanks for answering the questions and reviewing my code! I tried another approach, with the idea that if we can show that Start of RHS >= Start of LHS, and stride of LHS is positive and stride of RHS is negative, then the variables do not overflow(atleast before the last iteration) thus the exact backedge-count is computable(and the loop is finite).
I tried this on a sample code(CE), but I don't know why but Thanks again for the help! |
I don't see anything obviously wrong in your example; probably something simple is going wrong. Are you sure the expressions you're computing match the condition? Maybe try setting a breakpoint on ScalarEvolution::isImpliedCondBalancedTypes and step through. |
@efriedma-quic I have modified the code, maybe you can have a look at it? |
The general approach here seems fine; there are some special-cases you could optimize, but we can leave that for a followup. Please refactor the copy-pasted code so the relevant codepaths are shared. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
@efriedma-quic I copy-pasted the return logic because otherwise I had to push everything into a else branch(which would be pain to review and git-diff 😅) |
Please also include some test cases covering the various checks |
Maybe you can flatten the control flow using the Otherwise, it's not a big deal; we can always review the diff with whitespace turned off. |
Thanks for putting this patch! You might want to use https://alive2.llvm.org/ce/ to verify transformations. It is quite useful in finding corner cases. |
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.
Can you please add some tests in llvm/test/Analysis/ScalarEvolution?
Alive2 proof: https://alive2.llvm.org/ce/z/sXwLZY (updated) -> checks for validity for all start values as well as all strides(i.e. s1, s2 such that s1>0 and s2<0) |
@nikic I have added tests and verified the code using alive2. I hope everything alright! |
I guess I will broaden the scope and add optimisations in a follow up PR. |
Thinking about it a bit more, there's one thing the alive2 proof currently isn't testing: other exits from the loop (or infinite loops nested inside the loop). In those cases, even if we prove it's impossible to exit via this exit, we need to ensure that the returned backedge-taken count is greater than the number of times the loop actually iterates. |
@efriedma-quic I am not sure what exactly needs to be done. The case that you mentioned is true in general(i.e. for any type of loop), therefore shouldn't it be already handled by the previous code. Maybe I am misunderstanding you point here? If so, maybe if you could refer me to the original alive2 proof for computing backedge count (or something similar) it would point me in the right direction! |
@efriedma-quic From what I understand, if we could not find the exact backedge-count(like for the cases you mentioned) we return SCEVCouldNotCompute, which is considered greater than any computable value. |
The case I was thinking of is something like https://alive2.llvm.org/ce/z/kzxOYg . Alive2 thinks it's fine as long as we keep the strides-not-equal check. And yes, we can always return SCEVCouldNotCompute() if we can't prove there's a safe value to return. |
@efriedma-quic Interesting case that you mentioned! (good to know that it was not a problem) |
LGTM as i dont have any pending concerns. I'd defer to code owners to +1. Thanks for working on it as this enables vectorization of a class of loops. One suggestion would be to run llvm-testsuite or SPEC and see how many new loops got vectorized. Even without getting performance numbers, just |
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.
LGTM. Please don't forget to revise the commit message before you merge.
@nikic I have improved the commit message. (You can update it if you want!) I hope everything looks good! |
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.
Let's wait until Monday in case @nikic has any comments.
@mrdaybird Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested Please check whether problems have been caused by your change specifically, as How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
Thanks for help (with my first PR)! @efriedma-quic and @hiraditya! Learned a lot! Hopefully I can contribute more to LLVM. |
) Fixes llvm#92554 (std::reverse will auto-vectorize now) When calculating number of times a exit condition containing a comparison is executed, we mostly assume that RHS of comparison should be loop invariant, but it may be another add-recurrence. ~In that case, we can try the computation with `LHS = LHS - RHS` and `RHS = 0`.~ (It is not valid unless proven that it doesn't wrap) **Edit:** We can calculate back edge count for loop structure like: ```cpp left = left_start right = right_start while(left < right){ // ...do something... left += s1; // the stride of left is s1 (> 0) right -= s2; // the stride of right is -s2 (s2 > 0) } // left and right converge somewhere in the middle of their start values ``` We can calculate the backedge-count as ceil((End - left_start) /u (s1- (-s2)) where, End = max(left_start, right_start). **Alive2**: https://alive2.llvm.org/ce/z/ggxx58
Fixes #92554 (std::reverse will auto-vectorize now)
When calculating number of times a exit condition containing a comparison is executed, we mostly assume that RHS of comparison should be loop invariant, but it may be another add-recurrence.
In that case, we can try the computation with(It is not valid unless proven that it doesn't wrap)LHS = LHS - RHS
andRHS = 0
.Edit:
We can calculate back edge count for loop structure like:
We can calculate the backedge-count as ceil((End - left_start) /u (s1- (-s2)) where, End = max(left_start, right_start).
Alive2: https://alive2.llvm.org/ce/z/ggxx58