[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