[llvm] [GVN] Replace cmp with not instruction if inv cmp is found (PR #223171)
Andreas Jonson via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 14:23:01 PDT 2026
https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/223171
>From 04c79aa9803d5bb2e81543998525a83e21a8c004 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 12 Sep 2026 22:26:00 +0200
Subject: [PATCH 1/3] [GVN] pre commit test (NFC)
---
llvm/test/Transforms/GVN/inverse-cmp.ll | 52 +++++++++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 llvm/test/Transforms/GVN/inverse-cmp.ll
diff --git a/llvm/test/Transforms/GVN/inverse-cmp.ll b/llvm/test/Transforms/GVN/inverse-cmp.ll
new file mode 100644
index 0000000000000..192a37d9917d0
--- /dev/null
+++ b/llvm/test/Transforms/GVN/inverse-cmp.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=gvn -S < %s | FileCheck %s
+
+declare void @use(i1)
+
+define void @inverse_icmp(i32 %x,i32 %y) {
+; CHECK-LABEL: define void @inverse_icmp(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[UGT1:%.*]] = icmp ugt i32 [[X]], [[Y]]
+; CHECK-NEXT: call void @use(i1 [[UGT1]])
+; CHECK-NEXT: [[UGT1_NOT:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT: call void @use(i1 [[UGT1_NOT]])
+; CHECK-NEXT: ret void
+;
+ %ugt1 = icmp ugt i32 %x, %y
+ call void @use(i1 %ugt1)
+ %ule1 = icmp ule i32 %x, %y
+ call void @use(i1 %ule1)
+ ret void
+}
+
+define void @inverse_fcmp(double %x,double %y) {
+; CHECK-LABEL: define void @inverse_fcmp(
+; CHECK-SAME: double [[X:%.*]], double [[Y:%.*]]) {
+; CHECK-NEXT: [[OGT1:%.*]] = fcmp ogt double [[X]], [[Y]]
+; CHECK-NEXT: call void @use(i1 [[OGT1]])
+; CHECK-NEXT: [[OGT1_NOT:%.*]] = fcmp ule double [[X]], [[Y]]
+; CHECK-NEXT: call void @use(i1 [[OGT1_NOT]])
+; CHECK-NEXT: ret void
+;
+ %ogt1 = fcmp ogt double %x, %y
+ call void @use(i1 %ogt1)
+ %ule1 = fcmp ule double %x, %y
+ call void @use(i1 %ule1)
+ ret void
+}
+
+define void @inverse_icmp_swaped(i32 %x,i32 %y) {
+; CHECK-LABEL: define void @inverse_icmp_swaped(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[UGT1:%.*]] = icmp ugt i32 [[X]], [[Y]]
+; CHECK-NEXT: call void @use(i1 [[UGT1]])
+; CHECK-NEXT: [[UGT1_NOT:%.*]] = icmp uge i32 [[Y]], [[X]]
+; CHECK-NEXT: call void @use(i1 [[UGT1_NOT]])
+; CHECK-NEXT: ret void
+;
+ %ugt = icmp ugt i32 %x, %y
+ call void @use(i1 %ugt)
+ %ule = icmp uge i32 %y, %x
+ call void @use(i1 %ule)
+ ret void
+}
>From aa395283078e8ee23d97974d2f87142de4d72f9c Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 12 Sep 2026 22:28:41 +0200
Subject: [PATCH 2/3] [GVN] Replace cmp with not instruction if cmp inv is
found.
---
llvm/lib/Transforms/Scalar/GVN.cpp | 32 +++++++++++++++++--------
llvm/test/Transforms/GVN/inverse-cmp.ll | 6 ++---
2 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index c8989a4c8e0d4..acad4b311b2cf 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -3445,18 +3445,30 @@ bool GVNPass::processInstruction(Instruction *I) {
}
}
- // If the number we were assigned was a brand new VN, then we don't
- // need to do a lookup to see if the number already exists
- // somewhere in the domtree: it can't!
- if (Num >= NextNum) {
- LeaderTable.insert(Num, I, I->getParent());
- return false;
- }
-
// Perform fast-path value-number based elimination of values inherited from
- // dominators.
- Value *Repl = findLeader(I->getParent(), Num);
+ // dominators, unless if the number we were assigned was a brand new VN, then
+ // we don't need to do a lookup to see if the number already exists somewhere
+ // in the domtree: it can't!
+ Value *Repl = Num < NextNum ? findLeader(I->getParent(), Num) : nullptr;
if (!Repl) {
+ // substiut cmp instruction with not if possible.
+ if (CmpInst *Cmp = dyn_cast<CmpInst>(I)) {
+ uint32_t NextNumNot = VN.getNextUnusedValueNumber();
+ uint32_t NotNum =
+ VN.lookupOrAddCmp(Cmp->getOpcode(), Cmp->getInversePredicate(),
+ Cmp->getOperand(0), Cmp->getOperand(1));
+ if (NotNum < NextNumNot) {
+ Value *NotRepl = findLeader(I->getParent(), NotNum);
+ if (NotRepl && NotRepl != I) {
+ BinaryOperator *Not = BinaryOperator::CreateNot(
+ NotRepl, NotRepl->getName() + ".not", I->getIterator());
+ Not->setDebugLoc(I->getDebugLoc());
+ I->replaceAllUsesWith(Not);
+ salvageAndRemoveInstruction(I);
+ return true;
+ }
+ }
+ }
// Failure, just remember this instance for future use.
LeaderTable.insert(Num, I, I->getParent());
return false;
diff --git a/llvm/test/Transforms/GVN/inverse-cmp.ll b/llvm/test/Transforms/GVN/inverse-cmp.ll
index 192a37d9917d0..b874cd55b873a 100644
--- a/llvm/test/Transforms/GVN/inverse-cmp.ll
+++ b/llvm/test/Transforms/GVN/inverse-cmp.ll
@@ -8,7 +8,7 @@ define void @inverse_icmp(i32 %x,i32 %y) {
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: [[UGT1:%.*]] = icmp ugt i32 [[X]], [[Y]]
; CHECK-NEXT: call void @use(i1 [[UGT1]])
-; CHECK-NEXT: [[UGT1_NOT:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT: [[UGT1_NOT:%.*]] = xor i1 [[UGT1]], true
; CHECK-NEXT: call void @use(i1 [[UGT1_NOT]])
; CHECK-NEXT: ret void
;
@@ -24,7 +24,7 @@ define void @inverse_fcmp(double %x,double %y) {
; CHECK-SAME: double [[X:%.*]], double [[Y:%.*]]) {
; CHECK-NEXT: [[OGT1:%.*]] = fcmp ogt double [[X]], [[Y]]
; CHECK-NEXT: call void @use(i1 [[OGT1]])
-; CHECK-NEXT: [[OGT1_NOT:%.*]] = fcmp ule double [[X]], [[Y]]
+; CHECK-NEXT: [[OGT1_NOT:%.*]] = xor i1 [[OGT1]], true
; CHECK-NEXT: call void @use(i1 [[OGT1_NOT]])
; CHECK-NEXT: ret void
;
@@ -40,7 +40,7 @@ define void @inverse_icmp_swaped(i32 %x,i32 %y) {
; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
; CHECK-NEXT: [[UGT1:%.*]] = icmp ugt i32 [[X]], [[Y]]
; CHECK-NEXT: call void @use(i1 [[UGT1]])
-; CHECK-NEXT: [[UGT1_NOT:%.*]] = icmp uge i32 [[Y]], [[X]]
+; CHECK-NEXT: [[UGT1_NOT:%.*]] = xor i1 [[UGT1]], true
; CHECK-NEXT: call void @use(i1 [[UGT1_NOT]])
; CHECK-NEXT: ret void
;
>From 0e998ac700a8c66f1e5f247e0ffafb8c2debc2c7 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 12 Sep 2026 23:22:46 +0200
Subject: [PATCH 3/3] fixup! [GVN] Replace cmp with not instruction if cmp inv
is found.
---
llvm/test/Transforms/GVN/condprop.ll | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/test/Transforms/GVN/condprop.ll b/llvm/test/Transforms/GVN/condprop.ll
index eec1ea4a0abca..e963d8876f722 100644
--- a/llvm/test/Transforms/GVN/condprop.ll
+++ b/llvm/test/Transforms/GVN/condprop.ll
@@ -280,7 +280,7 @@ different:
define i1 @test6(i32 %x, i32 %y) {
; CHECK-LABEL: @test6(
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[CMP2]], true
; CHECK-NEXT: br i1 [[CMP]], label [[SAME:%.*]], label [[DIFFERENT:%.*]]
; CHECK: same:
; CHECK-NEXT: ret i1 false
@@ -302,7 +302,7 @@ different:
define i1 @test6_fp(float %x, float %y) {
; CHECK-LABEL: @test6_fp(
; CHECK-NEXT: [[CMP2:%.*]] = fcmp une float [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = fcmp oeq float [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[CMP2]], true
; CHECK-NEXT: br i1 [[CMP]], label [[SAME:%.*]], label [[DIFFERENT:%.*]]
; CHECK: same:
; CHECK-NEXT: ret i1 false
@@ -327,7 +327,7 @@ define i1 @test6_phi1(i1 %c, i32 %x, i32 %y) {
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[CMP_NOT]], true
; CHECK-NEXT: br i1 [[CMP]], label [[BB2]], label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[PHI:%.*]] = phi i1 [ false, [[BB1]] ], [ true, [[ENTRY:%.*]] ]
@@ -357,7 +357,7 @@ define i1 @test6_phi2(i1 %c, i32 %x, i32 %y) {
; CHECK-NEXT: br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[CMP_NOT]], true
; CHECK-NEXT: br i1 [[CMP]], label [[BB2]], label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[PHI:%.*]] = phi i1 [ false, [[BB1]] ], [ true, [[ENTRY:%.*]] ]
@@ -426,7 +426,7 @@ different:
define i1 @test8(i32 %x, i32 %y) {
; CHECK-LABEL: @test8(
; CHECK-NEXT: [[CMP2:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[CMP2]], true
; CHECK-NEXT: br i1 [[CMP]], label [[SAME:%.*]], label [[DIFFERENT:%.*]]
; CHECK: same:
; CHECK-NEXT: ret i1 false
@@ -448,7 +448,7 @@ different:
define i1 @test8_fp(float %x, float %y) {
; CHECK-LABEL: @test8_fp(
; CHECK-NEXT: [[CMP2:%.*]] = fcmp ule float [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = fcmp ogt float [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = xor i1 [[CMP2]], true
; CHECK-NEXT: br i1 [[CMP]], label [[SAME:%.*]], label [[DIFFERENT:%.*]]
; CHECK: same:
; CHECK-NEXT: ret i1 false
More information about the llvm-commits
mailing list