[llvm] [InstCombine] Fold select XOR with a masked false arm (PR #218094)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 21:02:40 PDT 2026
https://github.com/SNSuperNova created https://github.com/llvm/llvm-project/pull/218094
### Summary
Fold the following pattern:
```text
(select C, B, A) ^ (A & ~C) -> B & C
```
The select and the masked `and` are both required to have one use, so the
transform reduces the instruction count.
### Correctness
When `C` is true, the select produces `B` and the masked value is zero, so the
result is `B`. When `C` is false, both XOR operands are `A`, so the result is
zero. Therefore, the expression simplifies to `B & C`.
Under LLVM poison semantics, the target may be more defined than the source.
Alive2 was used to validate the required source-to-target refinement for the
affected regression cases.
### Profitability
The existing regression cases are reduced from four non-terminator SSA
instructions to a single `and`.
### Testing
- Built `opt` from LLVM main in Release mode with assertions enabled.
- Passed both directly affected InstCombine tests.
- Passed a 12-test related InstCombine lit subset covering adjacent
AND/OR/XOR/select transformations.
### Tool assistance
Assisted-by: OpenAI Codex
>From 47683ce9e43abc4ee9e98540eced2eabf512c138 Mon Sep 17 00:00:00 2001
From: Hayate <sse62840 at gmail.com>
Date: Sat, 22 Aug 2026 11:55:57 +0800
Subject: [PATCH] [InstCombine] Fold select XOR with a masked false arm
Fold (select C, B, A) ^ (A & ~C) to B & C when both the select and the masked AND are one-use. This reduces the instruction count and simplifies two existing logical-select regression cases to a single AND.
Alive2 verifies source-to-target refinement for the affected cases. The reverse direction does not hold because the target may be more defined for poison inputs.
Assisted-by: OpenAI Codex
---
.../Transforms/InstCombine/InstCombineAndOrXor.cpp | 11 ++++++++++-
.../InstCombine/logical-select-inseltpoison.ll | 7 ++-----
llvm/test/Transforms/InstCombine/logical-select.ll | 7 ++-----
3 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 7bc0d7d2f11b8..29bd3784c83b3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -5548,6 +5548,16 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (Instruction *FoldedLogic = foldBinOpSelectBinOp(I))
return FoldedLogic;
+ // (select C, B, A) ^ (A & ~C) --> B & C
+ // Both the select and the AND must be one-use, so this reduces instruction
+ // count.
+ Value *A, *B, *C;
+ if (match(&I,
+ m_c_Xor(m_OneUse(m_Select(m_Value(C), m_Value(B), m_Value(A))),
+ m_OneUse(m_c_And(m_Deferred(A),
+ m_Not(m_Deferred(C)))))))
+ return BinaryOperator::CreateAnd(B, C);
+
// Y ^ (X | Y) --> X & ~Y
// Y ^ (Y | X) --> X & ~Y
if (match(Op1, m_OneUse(m_c_Or(m_Value(X), m_Specific(Op0)))))
@@ -5570,7 +5580,6 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
match(Op0, m_OneUse(m_c_And(m_Value(X), m_Specific(Op1)))))
return BinaryOperator::CreateAnd(Op1, Builder.CreateNot(X));
- Value *A, *B, *C;
// (A ^ B) ^ (A | C) --> (~A & C) ^ B -- There are 4 commuted variants.
if (match(&I, m_c_Xor(m_OneUse(m_Xor(m_Value(A), m_Value(B))),
m_OneUse(m_c_Or(m_Deferred(A), m_Value(C))))))
diff --git a/llvm/test/Transforms/InstCombine/logical-select-inseltpoison.ll b/llvm/test/Transforms/InstCombine/logical-select-inseltpoison.ll
index 834d48f925305..1f699d1370433 100644
--- a/llvm/test/Transforms/InstCombine/logical-select-inseltpoison.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select-inseltpoison.ll
@@ -392,11 +392,8 @@ define i1 @bools_logical(i1 %a, i1 %b, i1 %c) {
define i1 @bools_multi_uses1(i1 %a, i1 %b, i1 %c) {
; CHECK-LABEL: @bools_multi_uses1(
-; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[C:%.*]], true
-; CHECK-NEXT: [[AND1:%.*]] = and i1 [[A:%.*]], [[NOT]]
-; CHECK-NEXT: [[OR:%.*]] = select i1 [[C]], i1 [[B:%.*]], i1 [[A]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[OR]], [[AND1]]
-; CHECK-NEXT: ret i1 [[XOR]]
+; CHECK-NEXT: [[R:%.*]] = and i1 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: ret i1 [[R]]
;
%not = xor i1 %c, -1
%and1 = and i1 %not, %a
diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll
index 85e8c98455c91..28cd7bad413cb 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -397,11 +397,8 @@ define i1 @bools_logical(i1 %a, i1 %b, i1 %c) {
define i1 @bools_multi_uses1(i1 %a, i1 %b, i1 %c) {
; CHECK-LABEL: @bools_multi_uses1(
-; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[C:%.*]], true
-; CHECK-NEXT: [[AND1:%.*]] = and i1 [[A:%.*]], [[NOT]]
-; CHECK-NEXT: [[OR:%.*]] = select i1 [[C]], i1 [[B:%.*]], i1 [[A]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[OR]], [[AND1]]
-; CHECK-NEXT: ret i1 [[XOR]]
+; CHECK-NEXT: [[R:%.*]] = and i1 [[B:%.*]], [[C:%.*]]
+; CHECK-NEXT: ret i1 [[R]]
;
%not = xor i1 %c, -1
%and1 = and i1 %not, %a
More information about the llvm-commits
mailing list