Skip to content

[ConstraintElim] Decompose 'or disjoint' like 'add nsw'. - #220990

Merged
fhahn merged 3 commits into
llvm:mainfrom
fhahn:ce-disjoint-or
Sep 4, 2026
Merged

[ConstraintElim] Decompose 'or disjoint' like 'add nsw'.#220990
fhahn merged 3 commits into
llvm:mainfrom
fhahn:ce-disjoint-or

Conversation

@fhahn

@fhahn fhahn commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with #220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.

https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox

Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with llvm#220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.

https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

Changes

Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with #220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.

https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+1-1)
  • (added) llvm/test/Transforms/ConstraintElimination/or-disjoint.ll (+159)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 93c3f2ff9900b..0e122b3a7480d 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -574,7 +574,7 @@ static Decomposition decompose(Value *V, const ConstraintInfo &Info,
         V = Op0;
     }
 
-    if (match(V, m_NSWAdd(m_Value(Op0), m_Value(Op1)))) {
+    if (match(V, m_NSWAddLike(m_Value(Op0), m_Value(Op1)))) {
       if (auto Decomp = MergeResults(Op0, Op1, IsSigned))
         return *Decomp;
       return V;
diff --git a/llvm/test/Transforms/ConstraintElimination/or-disjoint.ll b/llvm/test/Transforms/ConstraintElimination/or-disjoint.ll
new file mode 100644
index 0000000000000..4f3de9e5d476e
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/or-disjoint.ll
@@ -0,0 +1,159 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+declare void @use(i1)
+
+; Check or disjoint is treated as add nsw in queries.
+define void @test_or_disjoint_as_add_slt(i8 %a, i8 %b) {
+; CHECK-LABEL: define void @test_or_disjoint_as_add_slt(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_PLUS_3:%.*]] = add nsw i8 [[A]], 3
+; CHECK-NEXT:    [[PRE:%.*]] = icmp sge i8 [[A_PLUS_3]], [[B]]
+; CHECK-NEXT:    br i1 [[PRE]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[I_1:%.*]] = or disjoint i8 [[A]], 1
+; CHECK-NEXT:    [[C_1:%.*]] = icmp slt i8 [[I_1]], [[B]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[I_4:%.*]] = or disjoint i8 [[A]], 4
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    ret void
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    [[A_1:%.*]] = or disjoint i8 [[A]], 1
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[A_2:%.*]] = or disjoint i8 [[A]], 2
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[A_3:%.*]] = or disjoint i8 [[A]], 3
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[A_4:%.*]] = or disjoint i8 [[A]], 4
+; CHECK-NEXT:    [[C_2:%.*]] = icmp slt i8 [[A_4]], [[B]]
+; CHECK-NEXT:    call void @use(i1 [[C_2]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a.plus.3 = add nsw i8 %a, 3
+  %pre = icmp sge i8 %a.plus.3, %b
+  br i1 %pre, label %then, label %else
+
+then:
+  %i.1 = or disjoint i8 %a, 1
+  %c.1 = icmp slt i8 %i.1, %b
+  call void @use(i1 %c.1)
+  %i.4 = or disjoint i8 %a, 4
+  %f.1 = icmp slt i8 %i.4, %b
+  call void @use(i1 %f.1)
+  ret void
+
+else:
+  %a.1 = or disjoint i8 %a, 1
+  %t.1 = icmp slt i8 %a.1, %b
+  call void @use(i1 %t.1)
+  %a.2 = or disjoint i8 %a, 2
+  %t.2 = icmp slt i8 %a.2, %b
+  call void @use(i1 %t.2)
+  %a.3 = or disjoint i8 %a, 3
+  %t.3 = icmp slt i8 %a.3, %b
+  call void @use(i1 %t.3)
+  %a.4 = or disjoint i8 %a, 4
+  %c.2 = icmp slt i8 %a.4, %b
+  call void @use(i1 %c.2)
+  ret void
+}
+
+define void @test_or_disjoint_as_add_variable_operands(i8 %a, i8 %b, i8 %c) {
+; CHECK-LABEL: define void @test_or_disjoint_as_add_variable_operands(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]], i8 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_PLUS_B:%.*]] = add nsw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[PRE:%.*]] = icmp sge i8 [[A_PLUS_B]], [[C]]
+; CHECK-NEXT:    br i1 [[PRE]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[OR_1:%.*]] = or disjoint i8 [[A]], [[B]]
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    ret void
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    [[OR_2:%.*]] = or disjoint i8 [[A]], [[B]]
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a.plus.b = add nsw i8 %a, %b
+  %pre = icmp sge i8 %a.plus.b, %c
+  br i1 %pre, label %then, label %else
+
+then:
+  %or.1 = or disjoint i8 %a, %b
+  %f.1 = icmp slt i8 %or.1, %c
+  call void @use(i1 %f.1)
+  ret void
+
+else:
+  %or.2 = or disjoint i8 %a, %b
+  %t.1 = icmp slt i8 %or.2, %c
+  call void @use(i1 %t.1)
+  ret void
+}
+
+; Check or disjoint is treated as add nsw when adding facts.
+define void @test_or_disjoint_as_add_fact(i8 %a, i8 %b) {
+; CHECK-LABEL: define void @test_or_disjoint_as_add_fact(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_OR_4:%.*]] = or disjoint i8 [[A]], 4
+; CHECK-NEXT:    [[PRE:%.*]] = icmp sle i8 [[A_OR_4]], [[B]]
+; CHECK-NEXT:    br i1 [[PRE]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    ret void
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    [[C_1:%.*]] = icmp slt i8 [[A]], [[B]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a.or.4 = or disjoint i8 %a, 4
+  %pre = icmp sle i8 %a.or.4, %b
+  br i1 %pre, label %then, label %else
+
+then:
+  %t.1 = icmp slt i8 %a, %b
+  call void @use(i1 %t.1)
+  ret void
+
+else:
+  %c.1 = icmp slt i8 %a, %b
+  call void @use(i1 %c.1)
+  ret void
+}
+
+define void @test_or_without_disjoint_not_decomposed(i8 %a, i8 %b) {
+; CHECK-LABEL: define void @test_or_without_disjoint_not_decomposed(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_OR_4:%.*]] = or i8 [[A]], 4
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sle i8 [[A_OR_4]], [[B]]
+; CHECK-NEXT:    br i1 [[C_1]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[C_2:%.*]] = icmp slt i8 [[A]], [[B]]
+; CHECK-NEXT:    call void @use(i1 [[C_2]])
+; CHECK-NEXT:    ret void
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[A]], [[B]]
+; CHECK-NEXT:    call void @use(i1 [[C_3]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a.or.4 = or i8 %a, 4
+  %c.1 = icmp sle i8 %a.or.4, %b
+  br i1 %c.1, label %then, label %else
+
+then:
+  %c.2 = icmp slt i8 %a, %b
+  call void @use(i1 %c.2)
+  ret void
+
+else:
+  %c.3 = icmp slt i8 %a, %b
+  call void @use(i1 %c.3)
+  ret void
+}

@dtcxzyw dtcxzyw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@nikic

nikic commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Do you plan to also make the corresponding unsigned/nuw change?

I am not sure if the compile-time issue should block the patch.

As it just exposes an SLP issue, I don't think so.

@fhahn
fhahn enabled auto-merge (squash) September 4, 2026 08:54
@fhahn
fhahn merged commit 4cd5bc4 into llvm:main Sep 4, 2026
10 of 12 checks passed
@fhahn
fhahn deleted the ce-disjoint-or branch September 4, 2026 09:18

@fhahn fhahn left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do you plan to also make the corresponding unsigned/nuw change?

For unsigned, we already had a pattern matching or disjoint explicitly. replaced with m_NUWAddLike in #221175

llvm-sync Bot pushed a commit to arm/arm-toolchain that referenced this pull request Sep 4, 2026
…#220990)

Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with llvm/llvm-project#220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.

https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox

PR: llvm/llvm-project#220990
llvm-upstreamsync Bot pushed a commit to qualcomm/cpullvm-toolchain that referenced this pull request Sep 4, 2026
…#220990)

Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with llvm/llvm-project#220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.

https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox

PR: llvm/llvm-project#220990
Iasonaskrpr pushed a commit to Iasonaskrpr/llvm-project that referenced this pull request Sep 4, 2026
Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with llvm#220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.


https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox

PR: llvm#220990
addmisol pushed a commit to addmisol/llvm-project2 that referenced this pull request Sep 6, 2026
Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with llvm#220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.


https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox

PR: llvm#220990
fhahn added a commit to fhahn/llvm-project that referenced this pull request Sep 7, 2026
'or disjoint' can be treated as 'add nuw nsw'. Update
solveBlockValueBinaryOp to try to treat 'or disjoint' as both 'add nuw
nsw' and binary or, taking the intersection as result. In some cases the
binary or logic can yield better results
(`@or_disjoint_known_bits_more_precise` test).

This updates LVI to support 'or disjoint' similar to
ConstraintElimination (llvm#221175,
llvm#220990)

Compile-time impact is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=137d8e684984df207576c8f24bd8743ddb71c5c9&to=94f20b7fe67d745a55f4ba31f4f15249e7e22f1c&stat=instructions:u

Enables additional simplifications in a number of real-world cases
dtcxzyw/llvm-opt-benchmark-nightly#1222.
fhahn added a commit to fhahn/llvm-project that referenced this pull request Sep 8, 2026
'or disjoint' can be treated as 'add nuw nsw'. Update
solveBlockValueBinaryOp to try to treat 'or disjoint' as both 'add nuw
nsw' and binary or, taking the intersection as result. In some cases the
binary or logic can yield better results
(`@or_disjoint_known_bits_more_precise` test).

This updates LVI to support 'or disjoint' similar to
ConstraintElimination (llvm#221175,
llvm#220990)

Compile-time impact is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=137d8e684984df207576c8f24bd8743ddb71c5c9&to=94f20b7fe67d745a55f4ba31f4f15249e7e22f1c&stat=instructions:u

Enables additional simplifications in a number of real-world cases
dtcxzyw/llvm-opt-benchmark-nightly#1222.
fhahn added a commit to fhahn/llvm-project that referenced this pull request Sep 8, 2026
'or disjoint' can be treated as 'add nuw nsw'. Update
solveBlockValueBinaryOp to try to treat 'or disjoint' as both 'add nuw
nsw' and binary or, taking the intersection as result. In some cases the
binary or logic can yield better results
(`@or_disjoint_known_bits_more_precise` test).

This updates LVI to support 'or disjoint' similar to
ConstraintElimination (llvm#221175,
llvm#220990)

Compile-time impact is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=137d8e684984df207576c8f24bd8743ddb71c5c9&to=94f20b7fe67d745a55f4ba31f4f15249e7e22f1c&stat=instructions:u

Enables additional simplifications in a number of real-world cases
dtcxzyw/llvm-opt-benchmark-nightly#1222.
vadimkotov pushed a commit to vadimkotov/llvm-project that referenced this pull request Sep 8, 2026
Use m_NSWAddLike instead of m_NSWAdd to also handle 'or disjoint' in
signed decomposition.

Leads to a number of additional folds end-to-end:
dtcxzyw/llvm-opt-benchmark-nightly#1188

Compile-time impact is in the noise in almost all cases except lencod
with ThinLTO. On current main, compile-time for the workload regresses
by ~3%, but almost all additional time is spent in SLP: after some
branches got removed and BBs got merged got bigger, and SLP compile-time
scales super-linearly in block size in some cases.

Together with llvm#220968 the
regression goes down to ~+1.18% on the workload. I have 2 more prototype
patches that reduce the overhead by another ~0.30%. I am not sure if the
compile-time issue should block the patch.


https://llvm-compile-time-tracker.com/compare.php?from=ffe87191eb7090340f12056455104145670ed7e8&to=c073db66b714d3dd26c573ad8371ab8b117890a9&stat=instructions:u

Alive2 Proof: https://alive2.llvm.org/ce/z/ZVFEox

PR: llvm#220990
fhahn added a commit to fhahn/llvm-project that referenced this pull request Sep 10, 2026
'or disjoint' can be treated as 'add nuw nsw'. Update
solveBlockValueBinaryOp to try to treat 'or disjoint' as both 'add nuw
nsw' and binary or, taking the intersection as result. In some cases the
binary or logic can yield better results
(`@or_disjoint_known_bits_more_precise` test).

This updates LVI to support 'or disjoint' similar to
ConstraintElimination (llvm#221175,
llvm#220990)

Compile-time impact is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=137d8e684984df207576c8f24bd8743ddb71c5c9&to=94f20b7fe67d745a55f4ba31f4f15249e7e22f1c&stat=instructions:u

Enables additional simplifications in a number of real-world cases
dtcxzyw/llvm-opt-benchmark-nightly#1222.
fhahn added a commit to fhahn/llvm-project that referenced this pull request Sep 10, 2026
'or disjoint' can be treated as 'add nuw nsw'. Update
solveBlockValueBinaryOp to try to treat 'or disjoint' as both 'add nuw
nsw' and binary or, taking the intersection as result. In some cases the
binary or logic can yield better results
(`@or_disjoint_known_bits_more_precise` test).

This updates LVI to support 'or disjoint' similar to
ConstraintElimination (llvm#221175,
llvm#220990)

Compile-time impact is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=137d8e684984df207576c8f24bd8743ddb71c5c9&to=94f20b7fe67d745a55f4ba31f4f15249e7e22f1c&stat=instructions:u

Enables additional simplifications in a number of real-world cases
dtcxzyw/llvm-opt-benchmark-nightly#1222.
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.

3 participants