Skip to content

[ConstraintElimination] Decompose bitwise and or #126158

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

Closed
wants to merge 2 commits into from

Conversation

leewei05
Copy link
Contributor

@leewei05 leewei05 commented Feb 6, 2025

@leewei05 leewei05 requested review from nikic, fhahn and dtcxzyw February 6, 2025 23:50
@leewei05 leewei05 self-assigned this Feb 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 6, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Lee Wei (leewei05)

Changes

Alive2: https://alive2.llvm.org/ce/z/XUp6KG
Fixes #118114

@regehr


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

3 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+12)
  • (modified) llvm/test/Transforms/ConstraintElimination/and.ll (+88)
  • (modified) llvm/test/Transforms/ConstraintElimination/or.ll (+88)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index e0861fbedc560aa..78252ffa7ec785d 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -562,6 +562,18 @@ static Decomposition decompose(Value *V,
       }
     }
 
+    // (x | y) < 0 => (x < 0) || (y < 0)
+    if (match(V, m_Or(m_Value(Op0), m_Value(Op1)))) {
+      if (!isKnownNonNegative(Op0, DL) || !isKnownNonNegative(Op1, DL))
+        return MergeResults(Op0, Op1, IsSigned);
+    }
+
+    // (x & y) < 0 => (x < 0) && (y < 0)
+    if (match(V, m_And(m_Value(Op0), m_Value(Op1)))) {
+      if (!isKnownNonNegative(Op0, DL) && !isKnownNonNegative(Op1, DL))
+        return MergeResults(Op0, Op1, IsSigned);
+    }
+
     return {V, IsKnownNonNegative};
   }
 
diff --git a/llvm/test/Transforms/ConstraintElimination/and.ll b/llvm/test/Transforms/ConstraintElimination/and.ll
index f9824df3975e97a..9d89f8e5d372150 100644
--- a/llvm/test/Transforms/ConstraintElimination/and.ll
+++ b/llvm/test/Transforms/ConstraintElimination/and.ll
@@ -603,3 +603,91 @@ exit:
 
   ret i1 %r.10
 }
+
+define void @test_decompose_bitwise_and(i4 %x, i4 %y) {
+; CHECK-LABEL: @test_decompose_bitwise_and(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i4 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = icmp slt i4 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[AND]], label [[BB1:%.*]], label [[EXIT:%.*]]
+; CHECK:       bb1:
+; CHECK-NEXT:    [[F_1:%.*]] = icmp sge i4 [[X]], 0
+; CHECK-NEXT:    [[F_2:%.*]] = icmp sge i4 [[Y]], 0
+; CHECK-NEXT:    [[F_AND:%.*]] = and i1 false, [[F_2]]
+; CHECK-NEXT:    call void @use(i1 [[F_AND]])
+; CHECK-NEXT:    ret void
+; CHECK:       exit:
+; CHECK-NEXT:    [[F_3:%.*]] = icmp slt i4 [[X]], 0
+; CHECK-NEXT:    [[F_4:%.*]] = icmp slt i4 [[Y]], 0
+; CHECK-NEXT:    [[F_AND_2:%.*]] = and i1 false, [[F_4]]
+; CHECK-NEXT:    call void @use(i1 [[F_AND_2]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %1 = and i4 %y, %x
+  %and = icmp slt i4 %1, 0
+  br i1 %and, label %bb1, label %exit
+
+bb1:
+  %f.1 = icmp sge i4 %x, 0
+  %f.2 = icmp sge i4 %y, 0
+  %f.and = and i1 %f.1, %f.2
+  call void @use(i1 %f.and)
+  ret void
+
+exit:
+  %f.3 = icmp slt i4 %x, 0
+  %f.4 = icmp slt i4 %y, 0
+  %f.and.2 = and i1 %f.3, %f.4
+  call void @use(i1 %f.and.2)
+  ret void
+}
+
+define void @test_decompose_nested_bitwise_and(i4 %x, i4 %y, i4 %z) {
+; CHECK-LABEL: @test_decompose_nested_bitwise_and(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = and i4 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i4 [[TMP0]], [[Z:%.*]]
+; CHECK-NEXT:    [[AND:%.*]] = icmp slt i4 [[TMP1]], 0
+; CHECK-NEXT:    br i1 [[AND]], label [[BB1:%.*]], label [[EXIT:%.*]]
+; CHECK:       bb1:
+; CHECK-NEXT:    [[F_1:%.*]] = icmp sge i4 [[X]], 0
+; CHECK-NEXT:    [[F_2:%.*]] = icmp sge i4 [[Y]], 0
+; CHECK-NEXT:    [[F_3:%.*]] = icmp sge i4 [[Z]], 0
+; CHECK-NEXT:    [[F_AND:%.*]] = and i1 [[F_1]], [[F_2]]
+; CHECK-NEXT:    [[F_AND_2:%.*]] = and i1 [[F_AND]], false
+; CHECK-NEXT:    call void @use(i1 [[F_AND]])
+; CHECK-NEXT:    ret void
+; CHECK:       exit:
+; CHECK-NEXT:    [[F_4:%.*]] = icmp slt i4 [[X]], 0
+; CHECK-NEXT:    [[F_5:%.*]] = icmp slt i4 [[Y]], 0
+; CHECK-NEXT:    [[F_6:%.*]] = icmp slt i4 [[Z]], 0
+; CHECK-NEXT:    [[F_AND_3:%.*]] = and i1 [[F_4]], [[F_5]]
+; CHECK-NEXT:    [[F_AND_4:%.*]] = and i1 [[F_AND_3]], false
+; CHECK-NEXT:    call void @use(i1 [[F_AND_4]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %1 = and i4 %y, %x
+  %2 = and i4 %1, %z
+  %and = icmp slt i4 %2, 0
+  br i1 %and, label %bb1, label %exit
+
+bb1:
+  %f.1 = icmp sge i4 %x, 0
+  %f.2 = icmp sge i4 %y, 0
+  %f.3 = icmp sge i4 %z, 0
+  %f.and = and i1 %f.1, %f.2
+  %f.and.2 = and i1 %f.and, %f.3
+  call void @use(i1 %f.and)
+  ret void
+
+exit:
+  %f.4 = icmp slt i4 %x, 0
+  %f.5 = icmp slt i4 %y, 0
+  %f.6 = icmp slt i4 %z, 0
+  %f.and.3 = and i1 %f.4, %f.5
+  %f.and.4 = and i1 %f.and.3, %f.6
+  call void @use(i1 %f.and.4)
+  ret void
+}
diff --git a/llvm/test/Transforms/ConstraintElimination/or.ll b/llvm/test/Transforms/ConstraintElimination/or.ll
index b401d6f18136958..37fecb0348b3b1c 100644
--- a/llvm/test/Transforms/ConstraintElimination/or.ll
+++ b/llvm/test/Transforms/ConstraintElimination/or.ll
@@ -808,3 +808,91 @@ end:                                           ; preds = %entry
 
   ret void
 }
+
+define void @test_decompose_bitwise_or(i4 %x, i4 %y) {
+; CHECK-LABEL: @test_decompose_bitwise_or(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = or i4 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = icmp slt i4 [[TMP0]], 0
+; CHECK-NEXT:    br i1 [[OR]], label [[BB1:%.*]], label [[EXIT:%.*]]
+; CHECK:       bb1:
+; CHECK-NEXT:    [[F_1:%.*]] = icmp slt i4 [[X]], 0
+; CHECK-NEXT:    [[F_2:%.*]] = icmp slt i4 [[Y]], 0
+; CHECK-NEXT:    [[F_OR:%.*]] = or i1 true, [[F_2]]
+; CHECK-NEXT:    call void @use(i1 [[F_OR]])
+; CHECK-NEXT:    ret void
+; CHECK:       exit:
+; CHECK-NEXT:    [[F_3:%.*]] = icmp sge i4 [[X]], 0
+; CHECK-NEXT:    [[F_4:%.*]] = icmp sge i4 [[Y]], 0
+; CHECK-NEXT:    [[F_OR_2:%.*]] = or i1 true, [[F_4]]
+; CHECK-NEXT:    call void @use(i1 [[F_OR_2]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %1 = or i4 %y, %x
+  %or = icmp slt i4 %1, 0
+  br i1 %or, label %bb1, label %exit
+
+bb1:
+  %f.1 = icmp slt i4 %x, 0
+  %f.2 = icmp slt i4 %y, 0
+  %f.or = or i1 %f.1, %f.2
+  call void @use(i1 %f.or)
+  ret void
+
+exit:
+  %f.3 = icmp sge i4 %x, 0
+  %f.4 = icmp sge i4 %y, 0
+  %f.or.2 = or i1 %f.3, %f.4
+  call void @use(i1 %f.or.2)
+  ret void
+}
+
+define void @test_decompose_nested_bitwise_or(i4 %x, i4 %y, i4 %z) {
+; CHECK-LABEL: @test_decompose_nested_bitwise_or(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = or i4 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i4 [[TMP0]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR:%.*]] = icmp slt i4 [[TMP1]], 0
+; CHECK-NEXT:    br i1 [[OR]], label [[BB1:%.*]], label [[EXIT:%.*]]
+; CHECK:       bb1:
+; CHECK-NEXT:    [[F_1:%.*]] = icmp slt i4 [[X]], 0
+; CHECK-NEXT:    [[F_2:%.*]] = icmp slt i4 [[Y]], 0
+; CHECK-NEXT:    [[F_3:%.*]] = icmp slt i4 [[Z]], 0
+; CHECK-NEXT:    [[F_OR:%.*]] = or i1 [[F_1]], [[F_2]]
+; CHECK-NEXT:    [[F_OR_2:%.*]] = or i1 [[F_OR]], true
+; CHECK-NEXT:    call void @use(i1 [[F_OR_2]])
+; CHECK-NEXT:    ret void
+; CHECK:       exit:
+; CHECK-NEXT:    [[F_4:%.*]] = icmp sge i4 [[X]], 0
+; CHECK-NEXT:    [[F_5:%.*]] = icmp sge i4 [[Y]], 0
+; CHECK-NEXT:    [[F_6:%.*]] = icmp sge i4 [[Z]], 0
+; CHECK-NEXT:    [[F_OR_3:%.*]] = or i1 [[F_4]], [[F_5]]
+; CHECK-NEXT:    [[F_OR_4:%.*]] = or i1 [[F_OR_3]], true
+; CHECK-NEXT:    call void @use(i1 [[F_OR_4]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %1 = or i4 %y, %x
+  %2 = or i4 %1, %z
+  %or = icmp slt i4 %2, 0
+  br i1 %or, label %bb1, label %exit
+
+bb1:
+  %f.1 = icmp slt i4 %x, 0
+  %f.2 = icmp slt i4 %y, 0
+  %f.3 = icmp slt i4 %z, 0
+  %f.or = or i1 %f.1, %f.2
+  %f.or.2 = or i1 %f.or, %f.3
+  call void @use(i1 %f.or.2)
+  ret void
+
+exit:
+  %f.4 = icmp sge i4 %x, 0
+  %f.5 = icmp sge i4 %y, 0
+  %f.6 = icmp sge i4 %z, 0
+  %f.or.3 = or i1 %f.4, %f.5
+  %f.or.4 = or i1 %f.or.3, %f.6
+  call void @use(i1 %f.or.4)
+  ret void
+}

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.

  1. !isKnownNonNegative(X) doesn't mean X is negative. You should use isKnownNegative instead.
  2. MergeResults only works for add.
  3. decompose is used to decompose a value into the sum of variables and a constant offset. I guess @fhahn meant to decompose the constraint !(X | Y < 0) into X < 0 and Y < 0. You can do this in ConstraintInfo::addFact.

@leewei05
Copy link
Contributor Author

leewei05 commented Feb 7, 2025

  1. !isKnownNonNegative(X) doesn't mean X is negative. You should use isKnownNegative instead.
  2. MergeResults only works for add.
  3. decompose is used to decompose a value into the sum of variables and a constant offset. I guess @fhahn meant to decompose the constraint !(X | Y < 0) into X < 0 and Y < 0. You can do this in ConstraintInfo::addFact.

Thanks for the pointers! I should probably ask before implement this.. Let me try to re-implement this.

@leewei05 leewei05 closed this Feb 8, 2025
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.

constraint elimination should understand the (x<0)||(y<0) => (x|y)<0 canonicalization
3 participants