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

[ConstraintElim] Add facts implied by llvm.abs #73189

Merged

Conversation

alexander-shaposhnikov
Copy link
Collaborator

@alexander-shaposhnikov alexander-shaposhnikov commented Nov 23, 2023

Add "abs(x) >=s x" fact.

https://alive2.llvm.org/ce/z/gOrrU3

Test plan: ninja check-all
Happy to add more tests covering llvm.abs.

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 23, 2023

@llvm/pr-subscribers-llvm-transforms

Author: Alexander Shaposhnikov (alexander-shaposhnikov)

Changes

Add "abs(x) >= x" fact.

https://alive2.llvm.org/ce/z/gOrrU3

Test plan: ninja check-all
Happy to add more tests covering llvm.abs.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+10)
  • (added) llvm/test/Transforms/ConstraintElimination/abs.ll (+26)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 7aadd810c1da387..59aaa677ccbf9a9 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -995,6 +995,11 @@ void State::addInfoFor(BasicBlock &BB) {
       continue;
     }
 
+    if (match(&I, m_Intrinsic<Intrinsic::abs>())) {
+      WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), &I));
+      continue;
+    }
+
     Value *A, *B;
     CmpInst::Predicate Pred;
     // For now, just handle assumes with a single compare as condition.
@@ -1629,6 +1634,11 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
 
     ICmpInst::Predicate Pred;
     if (!CB.isConditionFact()) {
+      if (Value *X; match(CB.Inst, m_Intrinsic<Intrinsic::abs>(m_Value(X)))) {
+        AddFact(CmpInst::ICMP_SGE, CB.Inst, X);
+        continue;
+      }
+
       if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(CB.Inst)) {
         Pred = ICmpInst::getNonStrictPredicate(MinMax->getPredicate());
         AddFact(Pred, MinMax, MinMax->getLHS());
diff --git a/llvm/test/Transforms/ConstraintElimination/abs.ll b/llvm/test/Transforms/ConstraintElimination/abs.ll
new file mode 100644
index 000000000000000..1cf56c6e37a8c81
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/abs.ll
@@ -0,0 +1,26 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+define i1 @abs_false(i32 %arg) {
+; CHECK-LABEL: define i1 @abs_false(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT:    [[ABS:%.*]] = tail call i32 @llvm.abs.i32(i32 [[ARG]], i1 false)
+; CHECK-NEXT:    ret i1 true
+;
+  %abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 false)
+  %cmp = icmp sge i32 %abs, %arg
+  ret i1 %cmp
+}
+
+define i1 @abs_true(i32 %arg) {
+; CHECK-LABEL: define i1 @abs_true(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT:    [[ABS:%.*]] = tail call i32 @llvm.abs.i32(i32 [[ARG]], i1 true)
+; CHECK-NEXT:    ret i1 true
+;
+  %abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 true)
+  %cmp = icmp sge i32 %abs, %arg
+  ret i1 %cmp
+}
+
+declare i32 @llvm.abs.i32(i32, i1 immarg)

Copy link

github-actions bot commented Nov 23, 2023

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

@dtcxzyw
Copy link
Member

dtcxzyw commented Nov 23, 2023

What is the motivation of this patch? I think it is easy to implement this fold in InstSimplify.

@alexander-shaposhnikov
Copy link
Collaborator Author

@fhahn - this is indeed not a very frequent case, but the optimization kicked in a few times when i was testing it internally, so I don't have a strong opinion.

Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

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

Thanks for the patch! IMO it is very worthwhile to teach ConstraintElim about more intrinsics, as this can enable additional optimizations by the pass that cannot easily be done by other passes.

@alexander Shaposhnikov Assuming you still have the original motivating case, any chance you could add a more complicated test case that can only be handled by ConstraintElim?

@@ -995,6 +995,11 @@ void State::addInfoFor(BasicBlock &BB) {
continue;
}

if (match(&I, m_Intrinsic<Intrinsic::abs>())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we support a number of intrinsics, we should probably covert that to a switch on the intrinsic ID, so it is easy to see what intrinsics are handled at a glance, as well as making it obvious that they are all handled in the same way

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

@fhahn
Copy link
Contributor

fhahn commented Nov 30, 2023

(it also looks like the patch needs clang-format-diff)

@alexander-shaposhnikov
Copy link
Collaborator Author

Addressed comments.
P.S. The suggested edit from clang-format seems to be off (perhaps, the bot might be running an outdated version of the tool)
Screenshot 2023-12-03 at 4 58 05 AM

@alexander-shaposhnikov
Copy link
Collaborator Author

Address comments

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s

define i1 @abs_int_min_is_not_poison(i32 %arg) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you submit the tests separately and also add a version with constant arguments (both positive and negative)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

need to clarify: which constant arguments are you referring to ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ping @fhahn - happy to send a separate PR with the tests, but need a clarification regarding "constant arguments" - what did you mean

Copy link
Contributor

Choose a reason for hiding this comment

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

Constants to the llvm.abs calls

@alexander-shaposhnikov
Copy link
Collaborator Author

@fhahn - sorry about the delay, just sent a separate PR with tests:
#76374

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Dec 26, 2023
@dtcxzyw
Copy link
Member

dtcxzyw commented Dec 26, 2023

What is the motivation of this patch? Is this pattern from a real-world application? IMHO we should not add optimizations to improve the "coverage".

@alexander-shaposhnikov
Copy link
Collaborator Author

@dtcxzyw - yes, llvm.abs calls originate e.g. from std::abs and this showed up multiple times when i was testing the patch internally.

@dtcxzyw
Copy link
Member

dtcxzyw commented Dec 26, 2023

@dtcxzyw - yes, llvm.abs calls originate e.g. from std::abs and this showed up multiple times when i was testing the patch internally.

Great! Please add a more complicated test that can only be handled by ConstraintElim as @fhahn suggested.
Could you please provide the link to the code repo? I would like to add it into my benchmark :)

@alexander-shaposhnikov
Copy link
Collaborator Author

alexander-shaposhnikov commented Dec 26, 2023

@dtcxzyw , many thanks, actually the suggested test has already been added (GitHub seems to hide resolved comment threads) (see c13819c)

@dtcxzyw
Copy link
Member

dtcxzyw commented Dec 26, 2023

@dtcxzyw , many thanks, actually the suggested test has already been added (GitHub seems to hide resolved comment threads) (see c13819c)

All the test cases should be handled by InstSimplify.

Could you please provide the link to the code repo?

I mean the link to the `real-world application".

@alexander-shaposhnikov
Copy link
Collaborator Author

alexander-shaposhnikov commented Dec 26, 2023

@dtcxzyw

All the test cases should be handled by InstSimplify

The test that involves arithmetic was intentionally made simple. Regarding InstSimplify - unfortunately I would probably disagree with the approach. ConstraintElim is a more general / robust solution and iirc it was introduced for these purposes (as an alternative to expanding the complexity of patterns in InstCombine/InstSimplify), but I'd allow @nikic and @fhahn to weigh in / correct me. I think I've seen a few more cases where ConstraintElim stumbles on intrinsics, this was just the simplest one.

Could you please provide the link to the code repo?

The codebase is internal unfortunately.
I've just checked the list of github issues - it looks like it was independently noticed by other people here #72653

@dtcxzyw
Copy link
Member

dtcxzyw commented Dec 26, 2023

abs(x) <=u x also holds.
Alive2: https://alive2.llvm.org/ce/z/x4fPYp

@alexander-shaposhnikov
Copy link
Collaborator Author

P.S. I'd like to expand my previous comment a bit - I've just checked and currently InstSimplify doesn't handle these cases
(if the args are not constant):

./bin/opt -passes=instsimplify -S ~/abs.ll 
define i1 @abs_int_min_is_not_poison(i32 %arg) {
  %abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 false)
  %cmp = icmp sge i32 %abs, %arg
  ret i1 %cmp
}

define i1 @abs_int_min_is_poison(i32 %arg) {
  %abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 true)
  %cmp = icmp sge i32 %abs, %arg
  ret i1 %cmp
}

define i1 @abs_plus_one(i32 %arg) {
  %abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 true)
  %abs_plus_one = add nsw i32 %abs, 1
  %cmp = icmp sge i32 %abs_plus_one, %arg
  ret i1 %cmp
}

define i1 @abs_constant_negative_arg() {
  ret i1 true
}

define i1 @abs_constant_positive_arg() {
  ret i1 true
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.abs.i32(i32, i1 immarg) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

To me teaching ConstrainElim about intrinsics seems to be a better idea than putting more patterns into InstSimplify,
but maybe I'm missing something.

Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

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

Thanks for the latest update, looks mostly good, with a few small remaining comments inline and suggestions for follow-up simplifications.

It looks like the code-formatting checks failed, so it would be good to make sure the code is formatted correctly.

The test that involves arithmetic was intentionally made simple. Regarding InstSimplify - unfortunately I would probably disagree with the approach. ConstraintElim is a more general / robust solution and iirc it was introduced for these purposes (as an alternative to expanding the complexity of patterns in InstCombine/InstSimplify), but I'd allow @nikic and @fhahn to weigh in / correct me. I think I've seen a few more cases where ConstraintElim stumbles on intrinsics, this was just the simplest one.

I don't think it's a question of either/or, there should be value in handling it in both. InstCombine/InstSimplify is used at more different points in the pipeline (vs ConstraintElimination running once). The main advantage of support in ConstraintElimination isn't simplifying relatively simple stuff like

  %abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 false)
   %cmp = icmp sge i32 %abs, %arg

but more complicated expressions.

@alexander-shaposhnikov
Copy link
Collaborator Author

p.s. clang-format doesn't seem to format "if" properly:
Screenshot 2023-12-28 at 11 35 33 PM

alexander-shaposhnikov added a commit that referenced this pull request Dec 30, 2023
Add tests for llvm.abs.
This is a preparation for
#73189

Test plan: ninja check-all
Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

p.s. clang-format doesn't seem to format "if" properly: Screenshot 2023-12-28 at 11 35 33 PM

having multiple statements in an if is very uncommon in the codebase I think and should probably be avoided for consistency.

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp Outdated Show resolved Hide resolved
@fhahn
Copy link
Contributor

fhahn commented Dec 30, 2023

LGTM, thanks!

p.s. clang-format doesn't seem to format "if" properly: Screenshot 2023-12-28 at 11 35 33 PM

having multiple statements in an if is very uncommon in the codebase I think and should probably be avoided for consistency.

Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

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

Still LGTM, thanks!

@alexander-shaposhnikov alexander-shaposhnikov merged commit 3af59cf into llvm:main Jan 2, 2024
4 checks passed
@fhahn
Copy link
Contributor

fhahn commented Jan 8, 2024

@alexander-shaposhnikov are you planning on working on the suggestions for follow-ups?

@alexander-shaposhnikov
Copy link
Collaborator Author

@fhahn - yup, I'm planning to send a follow-up PR ~this week (if it's not urgent)

@fhahn
Copy link
Contributor

fhahn commented Jan 17, 2024

@fhahn - yup, I'm planning to send a follow-up PR ~this week (if it's not urgent)

Sounds great, just wanted to check, it's not urgent at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants