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

[SCCP] Extend visitBinaryOperator to overflowing binary ops #84470

Merged

Conversation

antoniofrighetto
Copy link
Contributor

Leverage more refined ranges results when handling overflowing binary operators.

@llvmbot
Copy link
Collaborator

llvmbot commented Mar 8, 2024

@llvm/pr-subscribers-function-specialization

@llvm/pr-subscribers-llvm-transforms

Author: Antonio Frighetto (antoniofrighetto)

Changes

Leverage more refined ranges results when handling overflowing binary operators.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/SCCPSolver.cpp (+12-1)
  • (modified) llvm/test/Transforms/SCCP/add-nuw-nsw-flags.ll (+29)
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index a185e8cd371c60..a0e522d9e555c7 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -1486,7 +1486,18 @@ void SCCPInstVisitor::visitBinaryOperator(Instruction &I) {
   // Try to simplify to a constant range.
   ConstantRange A = getConstantRange(V1State, I.getType());
   ConstantRange B = getConstantRange(V2State, I.getType());
-  ConstantRange R = A.binaryOp(cast<BinaryOperator>(&I)->getOpcode(), B);
+
+  auto *BO = cast<BinaryOperator>(&I);
+  ConstantRange R = ConstantRange::getEmpty(I.getType()->getScalarSizeInBits());
+  if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(BO)) {
+    bool HasNUW = OBO->hasNoUnsignedWrap();
+    bool HasNSW = OBO->hasNoSignedWrap();
+    unsigned Flags = (HasNUW ? OverflowingBinaryOperator::NoUnsignedWrap : 0) |
+                     (HasNSW ? OverflowingBinaryOperator::NoSignedWrap : 0);
+    R = A.overflowingBinaryOp(BO->getOpcode(), B, Flags);
+  } else {
+    R = A.binaryOp(BO->getOpcode(), B);
+  }
   mergeInValue(&I, ValueLatticeElement::getRange(R));
 
   // TODO: Currently we do not exploit special values that produce something
diff --git a/llvm/test/Transforms/SCCP/add-nuw-nsw-flags.ll b/llvm/test/Transforms/SCCP/add-nuw-nsw-flags.ll
index b8f5d5dba0c4b2..05d9acd1919629 100644
--- a/llvm/test/Transforms/SCCP/add-nuw-nsw-flags.ll
+++ b/llvm/test/Transforms/SCCP/add-nuw-nsw-flags.ll
@@ -240,3 +240,32 @@ then:
 else:
   ret i16 0
 }
+
+define i1 @test_add_nuw_sub(i32 %a) {
+; CHECK-LABEL: @test_add_nuw_sub(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[A:%.*]], 10000
+; CHECK-NEXT:    [[SUB:%.*]] = add i32 [[ADD]], -5000
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %add = add nuw i32 %a, 10000
+  %sub = add i32 %add, -5000
+  %cond = icmp ult i32 %sub, 5000
+  ret i1 %cond
+}
+
+define i1 @test_add_nsw_sub(i32 %a) {
+; CHECK-LABEL: @test_add_nsw_sub(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[A:%.*]], 10000
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i32 [[ADD]], -5000
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[SUB]], 5000
+; CHECK-NEXT:    ret i1 [[COND]]
+;
+entry:
+  %add = add nsw i32 %a, 10000
+  %sub = add i32 %add, -5000
+  %cond = icmp ult i32 %sub, 5000
+  ret i1 %cond
+}

@nikic
Copy link
Contributor

nikic commented Mar 8, 2024

Some compile-time overhead, but probably acceptable: https://llvm-compile-time-tracker.com/compare.php?from=eb8f379567e8d014194faefe02ce92813e237afc&to=52f204492d88fad02ef9d510e23be3ceee63671c&stat=instructions:u Wonder whether there is any optimization potential in the ConstantRange implementation.

@antoniofrighetto
Copy link
Contributor Author

Maybe acceptable, although don't look that good to me too, OTOH addWithNoWrap and subWithNoWrap look already quite optimized :/

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Mar 8, 2024
Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

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

LGTM.
BTW, would you like to fix the regression dtcxzyw/llvm-opt-benchmark#338 (comment)?
Alive2: https://alive2.llvm.org/ce/z/H2u9si

@dtcxzyw
Copy link
Member

dtcxzyw commented Mar 8, 2024

Another regression: https://github.com/dtcxzyw/llvm-opt-benchmark/pull/338/files#r1518193574
Alive2: https://alive2.llvm.org/ce/z/pZwdiS

@nikic
Copy link
Contributor

nikic commented Mar 8, 2024

LGTM. BTW, would you like to fix the regression dtcxzyw/llvm-opt-benchmark#338 (comment)? Alive2: https://alive2.llvm.org/ce/z/H2u9si

I was a bit confused about this because it does not seem like an improvement -- but you're only talking about the case where %b is constant, right?

@dtcxzyw
Copy link
Member

dtcxzyw commented Mar 9, 2024

LGTM. BTW, would you like to fix the regression dtcxzyw/llvm-opt-benchmark#338 (comment)? Alive2: https://alive2.llvm.org/ce/z/H2u9si

I was a bit confused about this because it does not seem like an improvement -- but you're only talking about the case where %b is constant, right?

Yeah, %b should be a positive constant.

@XChy
Copy link
Member

XChy commented Mar 9, 2024

Some compile-time overhead, but probably acceptable: https://llvm-compile-time-tracker.com/compare.php?from=eb8f379567e8d014194faefe02ce92813e237afc&to=52f204492d88fad02ef9d510e23be3ceee63671c&stat=instructions:u Wonder whether there is any optimization potential in the ConstantRange implementation.

Out of curiosity, I'm wondering why such minor modification on SCCP brings obivous overhead on compile-time. From my perspective, this patch only replaces add/sub with NoWrap implementation.

@nikic
Copy link
Contributor

nikic commented Mar 9, 2024

Some compile-time overhead, but probably acceptable: https://llvm-compile-time-tracker.com/compare.php?from=eb8f379567e8d014194faefe02ce92813e237afc&to=52f204492d88fad02ef9d510e23be3ceee63671c&stat=instructions:u Wonder whether there is any optimization potential in the ConstantRange implementation.

Out of curiosity, I'm wondering why such minor modification on SCCP brings obivous overhead on compile-time. From my perspective, this patch only replaces add/sub with NoWrap implementation.

Basically yes, but the NoWrap implementations are just more expensive. add() on ConstantRange is just two additions. With nowrap flags, you take a normal add() and combine it with multiple saturating additions and range intersections.

@antoniofrighetto
Copy link
Contributor Author

LGTM.
BTW, would you like to fix the regression dtcxzyw/llvm-opt-benchmark#338 (comment)?
Alive2: https://alive2.llvm.org/ce/z/H2u9si

Would it make sense to handle this in ConstraintElimination?

@nikic
Copy link
Contributor

nikic commented Mar 9, 2024

LGTM.
BTW, would you like to fix the regression dtcxzyw/llvm-opt-benchmark#338 (comment)?
Alive2: https://alive2.llvm.org/ce/z/H2u9si

Would it make sense to handle this in ConstraintElimination?

This looks more suited to InstCombine to me.

@dtcxzyw
Copy link
Member

dtcxzyw commented Mar 9, 2024

LGTM.
BTW, would you like to fix the regression dtcxzyw/llvm-opt-benchmark#338 (comment)?
Alive2: https://alive2.llvm.org/ce/z/H2u9si

Would it make sense to handle this in ConstraintElimination?

This looks more suited to InstCombine to me.

Yeah, we should fold sub 0, (udiv nneg X, nneg C) into sdiv nneg X, -C in InstCombine.

@antoniofrighetto
Copy link
Contributor Author

antoniofrighetto commented Mar 9, 2024

Another regression: https://github.com/dtcxzyw/llvm-opt-benchmark/pull/338/files#r1518193574
Alive2: https://alive2.llvm.org/ce/z/pZwdiS

Note that this already happens to be handled when isSignMask adheres to the type bitwidth (https://alive2.llvm.org/ce/z/KL7JYt) here:

if (C2->isSignMask()) {
Constant *Zero = Constant::getNullValue(X->getType());
auto NewPred = isICMP_NE ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_SGE;
return new ICmpInst(NewPred, X, Zero);
}

If it makes sense, we can extend it to handle the following missing case:

define i1 @src(i64 %a) {
entry:
  %rem817 = urem i64 %a, 1000
  %rem817.neg = sub nsw i64 0, %rem817
  %1 = and i64 %rem817.neg, 2147483648
  %cmp10.not = icmp eq i64 %1, 0
  ret i1 %cmp10.not
}

However, I think this holds as long as %rem817.neg is bounded up to a certain constant, and the output for computeKnownBits(And->getOperand(0), 0, And) for this instance might be suboptimal (zeroes 0, zeroes 1)? Is there a better way to handle this? Can take a look at VT, but would like to know if this makes sense.

@antoniofrighetto
Copy link
Contributor Author

@nikic, think we can merge this?

@nikic
Copy link
Contributor

nikic commented Mar 14, 2024

Yes, let's merge it.

@antoniofrighetto
Copy link
Contributor Author

Merging it. I think we can still solve the aforementioned regression though if needed (also, computeKnownBits output looks fine, I missed we only have the nsw flag).

Leverage more refined ranges results when handling overflowing
binary operators.
@antoniofrighetto antoniofrighetto merged commit 6ae4fcf into llvm:main Mar 14, 2024
3 of 4 checks passed
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

5 participants