[llvm] [InstCombine] Fix miscompilation in sinkNotIntoOtherHandOfLogicalOp (PR #226783)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 27 06:21:57 PDT 2026


https://github.com/andjo403 created https://github.com/llvm/llvm-project/pull/226783

Consider the following: `or X, (not (not X))` sinkNotIntoOtherHandOfLogicalOp calls `freelyInvert(X)` but as X is used in the not this call result in the new instruction is `and (not X), (not (not X)) -> and (not X), X` instead of the expected `and (not X), (not X)`

fixes https://github.com/llvm/llvm-project/issues/226504

>From 090eb4bb3a468724ec0aa13d4c00c68913fcec12 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 27 Sep 2026 14:00:55 +0200
Subject: [PATCH 1/2] [InstCombine] pre commit tests (NFC)

---
 .../InstCombine/pr226504-commuted.ll          | 22 +++++++++++++++++++
 llvm/test/Transforms/InstCombine/pr226504.ll  | 22 +++++++++++++++++++
 2 files changed, 44 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/pr226504-commuted.ll
 create mode 100644 llvm/test/Transforms/InstCombine/pr226504.ll

diff --git a/llvm/test/Transforms/InstCombine/pr226504-commuted.ll b/llvm/test/Transforms/InstCombine/pr226504-commuted.ll
new file mode 100644
index 0000000000000..6e2265e0704df
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/pr226504-commuted.ll
@@ -0,0 +1,22 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -debug-counter=instcombine-visit=4 -S | FileCheck %s
+
+define i8 @src(i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @src(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], i8 0, i8 1
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[SEL]] to i1
+; CHECK-NEXT:    [[TRUNC_NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC_NOT]], true
+; CHECK-NEXT:    [[OR_NOT:%.*]] = and i1 [[NOT]], [[TRUNC_NOT]]
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[OR_NOT]], i8 [[Y]], i8 [[X]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %sel = select i1 %c, i8 0, i8 1
+  %trunc = trunc i8 %sel to i1
+  %not = xor i1 %trunc, true
+  %not.not = xor i1 %not, true
+  %or = or i1 %not.not, %trunc
+  %ret = select i1 %or, i8 %x, i8 %y
+  ret i8 %ret
+}
diff --git a/llvm/test/Transforms/InstCombine/pr226504.ll b/llvm/test/Transforms/InstCombine/pr226504.ll
new file mode 100644
index 0000000000000..02f225ff2cff0
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/pr226504.ll
@@ -0,0 +1,22 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -debug-counter=instcombine-visit=4 -S | FileCheck %s
+
+define i8 @src(i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @src(
+; CHECK-SAME: i1 [[C:%.*]], i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], i8 0, i8 1
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[SEL]] to i1
+; CHECK-NEXT:    [[TRUNC_NOT:%.*]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC_NOT]], true
+; CHECK-NEXT:    [[OR_NOT:%.*]] = and i1 [[TRUNC_NOT]], [[NOT]]
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[OR_NOT]], i8 [[Y]], i8 [[X]]
+; CHECK-NEXT:    ret i8 [[RET]]
+;
+  %sel = select i1 %c, i8 0, i8 1
+  %trunc = trunc i8 %sel to i1
+  %not = xor i1 %trunc, true
+  %not.not = xor i1 %not, true
+  %or = or i1 %trunc, %not.not
+  %ret = select i1 %or, i8 %x, i8 %y
+  ret i8 %ret
+}

>From 84fb7bf10d5beb163b19d604a749b8e4d2094d71 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 27 Sep 2026 14:07:54 +0200
Subject: [PATCH 2/2] [InstCombine] Fix miscompilation in
 sinkNotIntoOtherHandOfLogicalOp

---
 llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp | 6 ++++++
 llvm/test/Transforms/InstCombine/pr226504-commuted.ll   | 6 ++----
 llvm/test/Transforms/InstCombine/pr226504.ll            | 6 ++----
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index ce585c69e9cf3..66c14cfba2056 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -5214,6 +5214,12 @@ bool InstCombinerImpl::sinkNotIntoOtherHandOfLogicalOp(Instruction &I) {
   } else
     return false;
 
+  // If the kept operand is defined as NOT(OpToInvert), freelyInvert(OpToInvert)
+  // will also flip the kept operand as a side effect of updating its uses,
+  // invalidating the assumption that it stays fixed while I is rewriten.
+  if (match(*OpToInvert == Op1 ? Op0 : Op1, m_Not(m_Specific(*OpToInvert))))
+    return false;
+
   // And can our users be adapted?
   if (!InstCombiner::canFreelyInvertAllUsersOf(&I, /*IgnoredUser=*/nullptr))
     return false;
diff --git a/llvm/test/Transforms/InstCombine/pr226504-commuted.ll b/llvm/test/Transforms/InstCombine/pr226504-commuted.ll
index 6e2265e0704df..7df0d8f6eba2d 100644
--- a/llvm/test/Transforms/InstCombine/pr226504-commuted.ll
+++ b/llvm/test/Transforms/InstCombine/pr226504-commuted.ll
@@ -6,10 +6,8 @@ define i8 @src(i1 %c, i8 %x, i8 %y) {
 ; CHECK-SAME: i1 [[C:%.*]], i8 [[X:%.*]], i8 [[Y:%.*]]) {
 ; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], i8 0, i8 1
 ; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[SEL]] to i1
-; CHECK-NEXT:    [[TRUNC_NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC_NOT]], true
-; CHECK-NEXT:    [[OR_NOT:%.*]] = and i1 [[NOT]], [[TRUNC_NOT]]
-; CHECK-NEXT:    [[RET:%.*]] = select i1 [[OR_NOT]], i8 [[Y]], i8 [[X]]
+; CHECK-NEXT:    [[OR:%.*]] = or i1 false, [[TRUNC]]
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[OR]], i8 [[X]], i8 [[Y]]
 ; CHECK-NEXT:    ret i8 [[RET]]
 ;
   %sel = select i1 %c, i8 0, i8 1
diff --git a/llvm/test/Transforms/InstCombine/pr226504.ll b/llvm/test/Transforms/InstCombine/pr226504.ll
index 02f225ff2cff0..2579789cb9027 100644
--- a/llvm/test/Transforms/InstCombine/pr226504.ll
+++ b/llvm/test/Transforms/InstCombine/pr226504.ll
@@ -6,10 +6,8 @@ define i8 @src(i1 %c, i8 %x, i8 %y) {
 ; CHECK-SAME: i1 [[C:%.*]], i8 [[X:%.*]], i8 [[Y:%.*]]) {
 ; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], i8 0, i8 1
 ; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[SEL]] to i1
-; CHECK-NEXT:    [[TRUNC_NOT:%.*]] = xor i1 [[TRUNC]], true
-; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[TRUNC_NOT]], true
-; CHECK-NEXT:    [[OR_NOT:%.*]] = and i1 [[TRUNC_NOT]], [[NOT]]
-; CHECK-NEXT:    [[RET:%.*]] = select i1 [[OR_NOT]], i8 [[Y]], i8 [[X]]
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[TRUNC]], false
+; CHECK-NEXT:    [[RET:%.*]] = select i1 [[OR]], i8 [[X]], i8 [[Y]]
 ; CHECK-NEXT:    ret i8 [[RET]]
 ;
   %sel = select i1 %c, i8 0, i8 1



More information about the llvm-commits mailing list