[llvm] [InstCombine] Improve `(icmp pred (and X, Y), ...)` fold. (PR #66787)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 17 22:57:05 PST 2023
https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/66787
>From ba3089f582dcf4c750b41814423c88f9bb2c67d6 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 13 Sep 2023 13:45:47 -0500
Subject: [PATCH 1/4] [InstCombine] Make `isFreeToInvert` check recursively.
Some Instructions (select/min/max) are inverted by just inverting the
operands. So the answer of whether they are free to invert is really
just whether the operands are free to invert.
Differential Revision: https://reviews.llvm.org/D159056
---
.../Transforms/InstCombine/InstCombiner.h | 61 +++++++++++--------
.../InstCombine/minmax-intrinsics.ll | 13 ++--
2 files changed, 41 insertions(+), 33 deletions(-)
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index f8b3874267ded3b..e9c1a2e645c972a 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -239,41 +239,50 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
/// uses of V and only keep uses of ~V.
///
/// See also: canFreelyInvertAllUsersOf()
- static bool isFreeToInvert(Value *V, bool WillInvertAllUses) {
+ static bool isFreeToInvert(Value *V, bool WillInvertAllUses,
+ unsigned Depth = 0) {
+ using namespace llvm::PatternMatch;
// ~(~(X)) -> X.
- if (match(V, m_Not(PatternMatch::m_Value())))
+ if (match(V, m_Not(m_Value())))
return true;
// Constants can be considered to be not'ed values.
- if (match(V, PatternMatch::m_AnyIntegralConstant()))
+ if (match(V, m_AnyIntegralConstant()))
return true;
+ if (Depth++ >= MaxAnalysisRecursionDepth)
+ return false;
+
+ // The rest of the cases require that we invert all uses so don't bother
+ // doing the analysis if we know we can't use the result.
+ if (!WillInvertAllUses)
+ return false;
+
// Compares can be inverted if all of their uses are being modified to use
// the ~V.
if (isa<CmpInst>(V))
- return WillInvertAllUses;
-
- // If `V` is of the form `A + Constant` then `-1 - V` can be folded into
- // `(-1 - Constant) - A` if we are willing to invert all of the uses.
- if (match(V, m_Add(PatternMatch::m_Value(), PatternMatch::m_ImmConstant())))
- return WillInvertAllUses;
-
- // If `V` is of the form `Constant - A` then `-1 - V` can be folded into
- // `A + (-1 - Constant)` if we are willing to invert all of the uses.
- if (match(V, m_Sub(PatternMatch::m_ImmConstant(), PatternMatch::m_Value())))
- return WillInvertAllUses;
-
- // Selects with invertible operands are freely invertible
- if (match(V,
- m_Select(PatternMatch::m_Value(), m_Not(PatternMatch::m_Value()),
- m_Not(PatternMatch::m_Value()))))
- return WillInvertAllUses;
-
- // Min/max may be in the form of intrinsics, so handle those identically
- // to select patterns.
- if (match(V, m_MaxOrMin(m_Not(PatternMatch::m_Value()),
- m_Not(PatternMatch::m_Value()))))
- return WillInvertAllUses;
+ return true;
+
+ Value *A, *B;
+ // If `V` is of the form `A + B` then `-1 - V` can be folded into
+ // `~B - A` or `~A - B` if we are willing to invert all of the uses.
+ if (match(V, m_Add(m_Value(A), m_Value(B))))
+ return isFreeToInvert(A, A->hasOneUse(), Depth) ||
+ isFreeToInvert(B, B->hasOneUse(), Depth);
+
+ // If `V` is of the form `A - B` then `-1 - V` can be folded into
+ // `~A + B` if we are willing to invert all of the uses.
+ if (match(V, m_Sub(m_Value(A), m_Value())))
+ return isFreeToInvert(A, A->hasOneUse(), Depth);
+
+ // LogicOps are special in that we canonicalize them at the cost of an
+ // instruction.
+ bool IsSelect = match(V, m_Select(m_Value(), m_Value(A), m_Value(B))) &&
+ !shouldAvoidAbsorbingNotIntoSelect(*cast<SelectInst>(V));
+ // Selects/min/max with invertible operands are freely invertible
+ if (IsSelect || match(V, m_MaxOrMin(m_Value(A), m_Value(B))))
+ return isFreeToInvert(A, A->hasOneUse(), Depth) &&
+ isFreeToInvert(B, B->hasOneUse(), Depth);
return false;
}
diff --git a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
index 3802036d2a715ac..6df9bb06ca56d2e 100644
--- a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
+++ b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
@@ -1494,14 +1494,13 @@ define i8 @freeToInvert_two_minmax_ops_use3(i8 %x, i8 %y, i8 %z, i8 %w) {
define i8 @sub_not_min_max(i8 %r, i8 %g, i8 %b) {
; CHECK-LABEL: @sub_not_min_max(
-; CHECK-NEXT: [[NOTR:%.*]] = xor i8 [[R:%.*]], -1
; CHECK-NEXT: [[NOTG:%.*]] = xor i8 [[G:%.*]], -1
; CHECK-NEXT: call void @use(i8 [[NOTG]])
; CHECK-NEXT: [[NOTB:%.*]] = xor i8 [[B:%.*]], -1
; CHECK-NEXT: call void @use(i8 [[NOTB]])
-; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[NOTR]], i8 [[NOTG]])
-; CHECK-NEXT: [[K:%.*]] = call i8 @llvm.smin.i8(i8 [[M]], i8 [[NOTB]])
-; CHECK-NEXT: [[CK:%.*]] = sub i8 [[NOTR]], [[K]]
+; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[R:%.*]], i8 [[G]])
+; CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[B]])
+; CHECK-NEXT: [[CK:%.*]] = sub i8 [[TMP2]], [[R]]
; CHECK-NEXT: ret i8 [[CK]]
;
%notr = xor i8 %r, -1
@@ -1523,9 +1522,9 @@ define i8 @sub_not_min_max_uses1(i8 %r, i8 %g, i8 %b) {
; CHECK-NEXT: call void @use(i8 [[NOTG]])
; CHECK-NEXT: [[NOTB:%.*]] = xor i8 [[B:%.*]], -1
; CHECK-NEXT: call void @use(i8 [[NOTB]])
-; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[NOTR]], i8 [[NOTG]])
-; CHECK-NEXT: [[K:%.*]] = call i8 @llvm.smin.i8(i8 [[M]], i8 [[NOTB]])
-; CHECK-NEXT: [[CK:%.*]] = sub i8 [[NOTR]], [[K]]
+; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smax.i8(i8 [[R]], i8 [[G]])
+; CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[B]])
+; CHECK-NEXT: [[CK:%.*]] = sub i8 [[TMP2]], [[R]]
; CHECK-NEXT: ret i8 [[CK]]
;
%notr = xor i8 %r, -1
>From cd0fba1d044a58acf778f718eeb94e891b8be12d Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Mon, 18 Sep 2023 14:59:58 -0500
Subject: [PATCH 2/4] [InstCombine] Add additional tests for free inversion;
NFC
---
.../Transforms/InstCombine/free-inversion.ll | 403 ++++++++++++++++++
1 file changed, 403 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/free-inversion.ll
diff --git a/llvm/test/Transforms/InstCombine/free-inversion.ll b/llvm/test/Transforms/InstCombine/free-inversion.ll
new file mode 100644
index 000000000000000..a2931fb9a081109
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/free-inversion.ll
@@ -0,0 +1,403 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i8 @llvm.smin.i8(i8, i8)
+declare i8 @llvm.umin.i8(i8, i8)
+declare i8 @llvm.smax.i8(i8, i8)
+declare i8 @llvm.umax.i8(i8, i8)
+
+declare void @use.i8(i8)
+
+define i8 @xor_1(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @xor_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[BA:%.*]] = xor i8 [[B]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ba = xor i8 %b, %a
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @xor_2(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @xor_2(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = xor i8 [[B]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ab = xor i8 %a, %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @xor_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @xor_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[Y:%.*]]
+; CHECK-NEXT: [[AB:%.*]] = xor i8 [[B]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %b = select i1 %c, i8 %nx, i8 %y
+ %ab = xor i8 %a, %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @add_1(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @add_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[BA:%.*]] = add i8 [[B]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ba = add i8 %b, %a
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @add_2(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @add_2(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = add i8 [[B]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ab = add i8 %a, %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @add_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @add_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = add i8 [[B]], [[A]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, %a
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ab = add i8 %a, %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @sub_1(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @sub_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[B]], -1
+; CHECK-NEXT: [[NOT_BA:%.*]] = add i8 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ba = sub i8 %b, %a
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @sub_2(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @sub_2(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[B_NEG_V:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[B_NEG:%.*]] = add i8 [[B_NEG_V]], 1
+; CHECK-NEXT: [[AB:%.*]] = add i8 [[B_NEG]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ab = sub i8 %a, %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @sub_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @sub_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: call void @use.i8(i8 [[NX]])
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[B]], -1
+; CHECK-NEXT: [[NOT_BA:%.*]] = add i8 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ call void @use.i8(i8 %nx)
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ba = sub i8 %b, %a
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @ashr_1(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @ashr_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[BA:%.*]] = ashr i8 [[B]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ba = ashr i8 %b, %a
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @ashr_2_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @ashr_2_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = ashr i8 [[A:%.*]], [[B]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ab = ashr i8 %a, %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @select_1(i1 %cc, i8 %na, i8 %aa, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @select_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[NA:%.*]], [[AA:%.*]]
+; CHECK-NEXT: [[A:%.*]] = xor i8 [[TMP1]], 45
+; CHECK-NEXT: [[AB:%.*]] = select i1 [[CC:%.*]], i8 [[A]], i8 [[B]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %nna = xor i8 %na, 45
+ %a = xor i8 %aa, %nna
+ %ab = select i1 %cc, i8 %a, i8 %b
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @select_2(i1 %cc, i8 %na, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @select_2(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[A:%.*]] = xor i8 [[NA:%.*]], 45
+; CHECK-NEXT: [[BA:%.*]] = select i1 [[CC:%.*]], i8 [[B]], i8 [[A]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %a = xor i8 %na, 45
+ %ba = select i1 %cc, i8 %b, i8 %a
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i1 @select_logic_or_fail(i1 %cc, i1 %c, i1 %x, i8 %y) {
+; CHECK-LABEL: @select_logic_or_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: [[YY:%.*]] = icmp eq i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i1 [[NX]], i1 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = select i1 [[CC:%.*]], i1 [[B]], i1 false
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i1 [[AB]], true
+; CHECK-NEXT: ret i1 [[NOT_AB]]
+;
+ %nx = xor i1 %x, -1
+ %yy = icmp eq i8 %y, 123
+ %b = select i1 %c, i1 %nx, i1 %yy
+ %ab = select i1 %cc, i1 %b, i1 false
+ %not_ab = xor i1 %ab, -1
+ ret i1 %not_ab
+}
+
+define i1 @select_logic_and_fail(i1 %cc, i1 %c, i1 %x, i8 %y) {
+; CHECK-LABEL: @select_logic_and_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT: [[YY:%.*]] = icmp eq i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i1 [[NX]], i1 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = select i1 [[CC:%.*]], i1 true, i1 [[B]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i1 [[AB]], true
+; CHECK-NEXT: ret i1 [[NOT_AB]]
+;
+ %nx = xor i1 %x, -1
+ %yy = icmp eq i8 %y, 123
+ %b = select i1 %c, i1 %nx, i1 %yy
+ %ab = select i1 %cc, i1 true, i1 %b
+ %not_ab = xor i1 %ab, -1
+ ret i1 %not_ab
+}
+
+define i8 @smin_1(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @smin_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[NNA:%.*]] = xor i8 [[NA:%.*]], -1
+; CHECK-NEXT: [[A:%.*]] = add i8 [[NNA]], [[AA:%.*]]
+; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.smin.i8(i8 [[A]], i8 [[B]])
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %nna = xor i8 %na, -1
+ %a = add i8 %aa, %nna
+ %ab = call i8 @llvm.smin.i8(i8 %a, i8 %b)
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @smin_1_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @smin_1_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.smin.i8(i8 [[A:%.*]], i8 [[B]])
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ab = call i8 @llvm.smin.i8(i8 %a, i8 %b)
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @umin_1_fail(i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @umin_1_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[Y:%.*]]
+; CHECK-NEXT: [[BA:%.*]] = call i8 @llvm.umin.i8(i8 [[B]], i8 85)
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %b = select i1 %c, i8 %nx, i8 %y
+ %ba = call i8 @llvm.umin.i8(i8 %b, i8 85)
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @smax_1(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @smax_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[NNA:%.*]] = xor i8 [[NA:%.*]], -1
+; CHECK-NEXT: [[A:%.*]] = sub i8 [[NNA]], [[AA:%.*]]
+; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 [[B]])
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %nna = xor i8 %na, -1
+ %a = sub i8 %nna, %aa
+ %ab = call i8 @llvm.smax.i8(i8 %a, i8 %b)
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @smax_1_fail(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @smax_1_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: call void @use.i8(i8 [[YY]])
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[NNA:%.*]] = xor i8 [[NA:%.*]], -1
+; CHECK-NEXT: [[A:%.*]] = sub i8 [[NNA]], [[AA:%.*]]
+; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 [[B]])
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: ret i8 [[NOT_AB]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ call void @use.i8(i8 %yy)
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %nna = xor i8 %na, -1
+ %a = sub i8 %nna, %aa
+ %ab = call i8 @llvm.smax.i8(i8 %a, i8 %b)
+ %not_ab = xor i8 %ab, -1
+ ret i8 %not_ab
+}
+
+define i8 @umax_1(i8 %na, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @umax_1(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: [[BA:%.*]] = call i8 @llvm.umax.i8(i8 [[B]], i8 85)
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ %ba = call i8 @llvm.umax.i8(i8 %b, i8 85)
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
+
+define i8 @umax_1_fail(i8 %na, i1 %c, i8 %x, i8 %y) {
+; CHECK-LABEL: @umax_1_fail(
+; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
+; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
+; CHECK-NEXT: call void @use.i8(i8 [[B]])
+; CHECK-NEXT: [[BA:%.*]] = call i8 @llvm.umax.i8(i8 [[B]], i8 85)
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: ret i8 [[NOT_BA]]
+;
+ %nx = xor i8 %x, -1
+ %yy = xor i8 %y, 123
+ %b = select i1 %c, i8 %nx, i8 %yy
+ call void @use.i8(i8 %b)
+ %ba = call i8 @llvm.umax.i8(i8 %b, i8 85)
+ %not_ba = xor i8 %ba, -1
+ ret i8 %not_ba
+}
>From 04a734f1e56ae263bb619ddd984137eaf53a1441 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Mon, 18 Sep 2023 15:12:51 -0500
Subject: [PATCH 3/4] [InstCombine] add `getFreeInverted` to perform folds for
free inversion of op
With the current logic of `if(isFreeToInvert(Op)) return Not(Op)` its
fairly easy to either 1) cause regressions or 2) infinite loops
if the folds we have for `Not(Op)` ever de-sync with the cases we
know are freely invertible.
This patch adds `getFreeInverted` which is able to build the free
inverted op along with check for free inversion to alleviate this
problem.
---
.../Transforms/InstCombine/InstCombiner.h | 118 +++++++++++++-----
.../InstCombine/InstCombineAndOrXor.cpp | 5 +
.../Transforms/InstCombine/free-inversion.ll | 110 +++++++---------
.../Transforms/InstCombine/icmp-of-or-x.ll | 6 +-
.../LoopVectorize/ARM/mve-selectandorcost.ll | 2 +-
5 files changed, 138 insertions(+), 103 deletions(-)
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index e9c1a2e645c972a..fcf72fd5ff9c678 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -233,58 +233,114 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
PatternMatch::m_Value()));
}
- /// Return true if the specified value is free to invert (apply ~ to).
- /// This happens in cases where the ~ can be eliminated. If WillInvertAllUses
- /// is true, work under the assumption that the caller intends to remove all
- /// uses of V and only keep uses of ~V.
- ///
- /// See also: canFreelyInvertAllUsersOf()
- static bool isFreeToInvert(Value *V, bool WillInvertAllUses,
- unsigned Depth = 0) {
+ /// Return nonnull value if V is free to invert (with condition) regarding
+ /// WillInvertAllUses.
+ /// If Builder is nonnull, it will return a simplified ~V
+ /// If Builder is null, it will return an arbitrary nonnull value (not
+ /// dereferenceable).
+ static Value *getFreelyInverted(Value *V, bool WillInvertAllUses,
+ BuilderTy *Builder, unsigned Depth = 0) {
using namespace llvm::PatternMatch;
+ static Value *const NonNull = reinterpret_cast<Value *>(uintptr_t(1));
// ~(~(X)) -> X.
- if (match(V, m_Not(m_Value())))
- return true;
+ Value *A, *B;
+ if (match(V, m_Not(m_Value(A))))
+ return A;
+ Constant *C;
// Constants can be considered to be not'ed values.
- if (match(V, m_AnyIntegralConstant()))
- return true;
+ if (match(V, m_ImmConstant(C)))
+ return ConstantExpr::getNot(C);
if (Depth++ >= MaxAnalysisRecursionDepth)
- return false;
+ return nullptr;
// The rest of the cases require that we invert all uses so don't bother
// doing the analysis if we know we can't use the result.
if (!WillInvertAllUses)
- return false;
+ return nullptr;
// Compares can be inverted if all of their uses are being modified to use
// the ~V.
- if (isa<CmpInst>(V))
- return true;
+ if (auto *I = dyn_cast<CmpInst>(V)) {
+ if (Builder != nullptr)
+ return Builder->CreateCmp(I->getInversePredicate(), I->getOperand(0),
+ I->getOperand(1));
+ return NonNull;
+ }
- Value *A, *B;
- // If `V` is of the form `A + B` then `-1 - V` can be folded into
- // `~B - A` or `~A - B` if we are willing to invert all of the uses.
- if (match(V, m_Add(m_Value(A), m_Value(B))))
- return isFreeToInvert(A, A->hasOneUse(), Depth) ||
- isFreeToInvert(B, B->hasOneUse(), Depth);
+ // If `V` is of the form `A + Constant` then `-1 - V` can be folded into
+ // `(-1 - Constant) - A` if we are willing to invert all of the uses.
+ if (match(V, m_Add(m_Value(A), m_Value(B)))) {
+ if (auto *BV = getFreelyInverted(B, B->hasOneUse(), Builder, Depth))
+ return Builder ? Builder->CreateSub(BV, A) : NonNull;
+ if (auto *AV = getFreelyInverted(A, A->hasOneUse(), Builder, Depth))
+ return Builder ? Builder->CreateSub(AV, B) : NonNull;
+ return nullptr;
+ }
- // If `V` is of the form `A - B` then `-1 - V` can be folded into
- // `~A + B` if we are willing to invert all of the uses.
- if (match(V, m_Sub(m_Value(A), m_Value())))
- return isFreeToInvert(A, A->hasOneUse(), Depth);
+ // If `V` is of the form `A ^ ~B` then `~(A ^ ~B)` can be folded
+ // into `A ^ B` if we are willing to invert all of the uses.
+ if (match(V, m_Xor(m_Value(A), m_Value(B)))) {
+ if (auto *BV = getFreelyInverted(B, B->hasOneUse(), Builder, Depth))
+ return Builder ? Builder->CreateXor(A, BV) : NonNull;
+ if (auto *AV = getFreelyInverted(A, A->hasOneUse(), Builder, Depth))
+ return Builder ? Builder->CreateXor(AV, B) : NonNull;
+ return nullptr;
+ }
+ // If `V` is of the form `Constant - A` then `-1 - V` can be folded into
+ // `A + (-1 - Constant)` if we are willing to invert all of the uses.
+ if (match(V, m_Sub(m_Value(A), m_Value(B)))) {
+ if (auto *AV = getFreelyInverted(A, A->hasOneUse(), Builder, Depth))
+ return Builder ? Builder->CreateAdd(AV, B) : NonNull;
+ return nullptr;
+ }
+
+ // If `V` is of the form `(~A) s>> B` then `~((~A) s>> B)` can be folded
+ // into `A s>> B` if we are willing to invert all of the uses.
+ if (match(V, m_AShr(m_Value(A), m_Value(B)))) {
+ if (auto *AV = getFreelyInverted(A, A->hasOneUse(), Builder, Depth))
+ return Builder ? Builder->CreateAShr(AV, B) : NonNull;
+ return nullptr;
+ }
+
+ Value *Cond;
// LogicOps are special in that we canonicalize them at the cost of an
// instruction.
- bool IsSelect = match(V, m_Select(m_Value(), m_Value(A), m_Value(B))) &&
+ bool IsSelect = match(V, m_Select(m_Value(Cond), m_Value(A), m_Value(B))) &&
!shouldAvoidAbsorbingNotIntoSelect(*cast<SelectInst>(V));
// Selects/min/max with invertible operands are freely invertible
- if (IsSelect || match(V, m_MaxOrMin(m_Value(A), m_Value(B))))
- return isFreeToInvert(A, A->hasOneUse(), Depth) &&
- isFreeToInvert(B, B->hasOneUse(), Depth);
+ if (IsSelect || match(V, m_MaxOrMin(m_Value(A), m_Value(B)))) {
+ if (!getFreelyInverted(A, A->hasOneUse(), /*Builder*/ nullptr, Depth))
+ return nullptr;
+ if (Value *NotB = getFreelyInverted(B, B->hasOneUse(), Builder, Depth)) {
+ if (Builder != nullptr) {
+ Value *NotA = getFreelyInverted(A, A->hasOneUse(), Builder, Depth);
+ assert(
+ NotA != nullptr &&
+ "Unable to build inverted value for known freely invertable op");
+ if (auto *II = dyn_cast<IntrinsicInst>(V))
+ return Builder->CreateBinaryIntrinsic(
+ getInverseMinMaxIntrinsic(II->getIntrinsicID()), NotA, NotB);
+ return Builder->CreateSelect(Cond, NotA, NotB);
+ }
+ return NonNull;
+ }
+ }
- return false;
+ return nullptr;
+ }
+
+ /// Return true if the specified value is free to invert (apply ~ to).
+ /// This happens in cases where the ~ can be eliminated. If WillInvertAllUses
+ /// is true, work under the assumption that the caller intends to remove all
+ /// uses of V and only keep uses of ~V.
+ ///
+ /// See also: canFreelyInvertAllUsersOf()
+ static bool isFreeToInvert(Value *V, bool WillInvertAllUses) {
+ return getFreelyInverted(V, WillInvertAllUses, /*Builder*/ nullptr,
+ /*Depth*/ 0) != nullptr;
}
/// Given i1 V, can every user of V be freely adapted if V is changed to !V ?
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 46af9bf5eed003a..0750c1529d0b3c2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4374,6 +4374,11 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
if (Instruction *NewXor = foldNotXor(I, Builder))
return NewXor;
+ // TODO: Could handle multi-use better by checking if all uses of NotOp (other
+ // than I) can be inverted.
+ if (Value *R = getFreelyInverted(NotOp, NotOp->hasOneUse(), &Builder))
+ return replaceInstUsesWith(I, R);
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/free-inversion.ll b/llvm/test/Transforms/InstCombine/free-inversion.ll
index a2931fb9a081109..acafc1f4c4048a5 100644
--- a/llvm/test/Transforms/InstCombine/free-inversion.ll
+++ b/llvm/test/Transforms/InstCombine/free-inversion.ll
@@ -10,11 +10,9 @@ declare void @use.i8(i8)
define i8 @xor_1(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @xor_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[BA:%.*]] = xor i8 [[B]], [[A:%.*]]
-; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[B_NOT:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[B_NOT]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -27,11 +25,9 @@ define i8 @xor_1(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @xor_2(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @xor_2(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[AB:%.*]] = xor i8 [[B]], [[A:%.*]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[B_NOT:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[B_NOT]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -59,11 +55,9 @@ define i8 @xor_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @add_1(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @add_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[BA:%.*]] = add i8 [[B]], [[A:%.*]]
-; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = sub i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -76,11 +70,9 @@ define i8 @add_1(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @add_2(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @add_2(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[AB:%.*]] = add i8 [[B]], [[A:%.*]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = sub i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -110,11 +102,9 @@ define i8 @add_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @sub_1(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @sub_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[B]], -1
-; CHECK-NEXT: [[NOT_BA:%.*]] = add i8 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = add i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -129,9 +119,8 @@ define i8 @sub_2(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @sub_2(
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
; CHECK-NEXT: [[B_NEG_V:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
-; CHECK-NEXT: [[B_NEG:%.*]] = add i8 [[B_NEG_V]], 1
-; CHECK-NEXT: [[AB:%.*]] = add i8 [[B_NEG]], [[A:%.*]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = add i8 [[B_NEG_V]], [[A:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = sub i8 -2, [[TMP2]]
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -145,11 +134,10 @@ define i8 @sub_2(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @sub_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @sub_fail(
; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
; CHECK-NEXT: call void @use.i8(i8 [[NX]])
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[B]], -1
-; CHECK-NEXT: [[NOT_BA:%.*]] = add i8 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = add i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -163,11 +151,9 @@ define i8 @sub_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @ashr_1(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @ashr_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[BA:%.*]] = ashr i8 [[B]], [[A:%.*]]
-; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = ashr i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -197,13 +183,11 @@ define i8 @ashr_2_fail(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @select_1(i1 %cc, i8 %na, i8 %aa, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @select_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[NA:%.*]], [[AA:%.*]]
-; CHECK-NEXT: [[A:%.*]] = xor i8 [[TMP1]], 45
-; CHECK-NEXT: [[AB:%.*]] = select i1 [[CC:%.*]], i8 [[A]], i8 [[B]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP2]]
+; CHECK-NEXT: [[TMP4:%.*]] = xor i8 [[TMP1]], -46
+; CHECK-NEXT: [[NOT_AB:%.*]] = select i1 [[CC:%.*]], i8 [[TMP4]], i8 [[TMP3]]
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -218,12 +202,10 @@ define i8 @select_1(i1 %cc, i8 %na, i8 %aa, i1 %c, i8 %x, i8 %y) {
define i8 @select_2(i1 %cc, i8 %na, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @select_2(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[A:%.*]] = xor i8 [[NA:%.*]], 45
-; CHECK-NEXT: [[BA:%.*]] = select i1 [[CC:%.*]], i8 [[B]], i8 [[A]]
-; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[NA:%.*]], -46
+; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP2]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = select i1 [[CC:%.*]], i8 [[TMP3]], i8 [[TMP1]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -271,13 +253,10 @@ define i1 @select_logic_and_fail(i1 %cc, i1 %c, i1 %x, i8 %y) {
define i8 @smin_1(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @smin_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[NNA:%.*]] = xor i8 [[NA:%.*]], -1
-; CHECK-NEXT: [[A:%.*]] = add i8 [[NNA]], [[AA:%.*]]
-; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.smin.i8(i8 [[A]], i8 [[B]])
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = sub i8 [[NA:%.*]], [[AA:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP2]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[TMP3]])
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -324,13 +303,10 @@ define i8 @umin_1_fail(i1 %c, i8 %x, i8 %y) {
define i8 @smax_1(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @smax_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[NNA:%.*]] = xor i8 [[NA:%.*]], -1
-; CHECK-NEXT: [[A:%.*]] = sub i8 [[NNA]], [[AA:%.*]]
-; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 [[B]])
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[AB]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[NA:%.*]], [[AA:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP2]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = call i8 @llvm.smin.i8(i8 [[TMP1]], i8 [[TMP3]])
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -368,11 +344,9 @@ define i8 @smax_1_fail(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
define i8 @umax_1(i8 %na, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @umax_1(
-; CHECK-NEXT: [[NX:%.*]] = xor i8 [[X:%.*]], -1
-; CHECK-NEXT: [[YY:%.*]] = xor i8 [[Y:%.*]], 123
-; CHECK-NEXT: [[B:%.*]] = select i1 [[C:%.*]], i8 [[NX]], i8 [[YY]]
-; CHECK-NEXT: [[BA:%.*]] = call i8 @llvm.umax.i8(i8 [[B]], i8 85)
-; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[BA]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = call i8 @llvm.umin.i8(i8 [[TMP2]], i8 -86)
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
index 839aa98a8b24e2f..4b8df439b846fb6 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
@@ -121,9 +121,9 @@ define i1 @or_ne_notY_eq_1s(i8 %x, i8 %y) {
define i1 @or_ne_notY_eq_1s_fail_bad_not(i8 %x, i8 %y) {
; CHECK-LABEL: @or_ne_notY_eq_1s_fail_bad_not(
-; CHECK-NEXT: [[NY:%.*]] = xor i8 [[Y:%.*]], -2
-; CHECK-NEXT: [[OR:%.*]] = or i8 [[NY]], [[X:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[OR]], [[X]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], [[X:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[TMP2]], -1
; CHECK-NEXT: ret i1 [[CMP]]
;
%ny = xor i8 %y, -2
diff --git a/llvm/test/Transforms/LoopVectorize/ARM/mve-selectandorcost.ll b/llvm/test/Transforms/LoopVectorize/ARM/mve-selectandorcost.ll
index 5d85a4cd73fddfd..c4d1607ff640af5 100644
--- a/llvm/test/Transforms/LoopVectorize/ARM/mve-selectandorcost.ll
+++ b/llvm/test/Transforms/LoopVectorize/ARM/mve-selectandorcost.ll
@@ -86,7 +86,7 @@ define float @test(ptr nocapture readonly %pA, ptr nocapture readonly %pB, i32 %
; CHECK-NEXT: [[ACCUM_1]] = phi float [ [[ADD4]], [[IF_THEN]] ], [ [[ACCUM_017]], [[WHILE_BODY]] ]
; CHECK-NEXT: [[DEC]] = add i32 [[BLOCKSIZE_ADDR_018]], -1
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[DEC]], 0
-; CHECK-NEXT: br i1 [[CMP_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], !llvm.loop [[LOOP2:![0-9]+]]
+; CHECK-NEXT: br i1 [[CMP_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], !llvm.loop [[LOOP3:![0-9]+]]
; CHECK: while.end:
; CHECK-NEXT: [[ACCUM_0_LCSSA:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[ACCUM_1]], [[IF_END]] ], [ [[TMP14]], [[MIDDLE_BLOCK]] ]
; CHECK-NEXT: ret float [[ACCUM_0_LCSSA]]
>From 35dbbb9c261b8ac664c7fd74588cc55a039b9da1 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Sat, 18 Nov 2023 00:17:02 -0600
Subject: [PATCH 4/4] [InstCombine] Replace `isFreeToInvert` + `CreateNot` with
`getFreelyInverted`
This is nearly an NFC, the only change is potentially to order that
values are created/names.
Otherwise it is a slight speed boost/simplification to avoid having to
go through the `getFreelyInverted` recursive logic twice to simplify
the extra `not` op.
---
.../InstCombine/InstCombineAddSub.cpp | 14 +++---
.../InstCombine/InstCombineAndOrXor.cpp | 50 ++++++++++++-------
.../InstCombine/InstCombineCalls.cpp | 12 ++---
.../InstCombine/InstCombineCompares.cpp | 21 ++++----
.../InstCombine/InstCombineSelect.cpp | 20 ++++----
.../Transforms/InstCombine/free-inversion.ll | 24 ++++-----
.../InstCombine/minmax-intrinsics.ll | 6 +--
llvm/test/Transforms/InstCombine/pr38915.ll | 6 +--
llvm/test/Transforms/InstCombine/xor.ll | 24 ++++-----
9 files changed, 96 insertions(+), 81 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 318992b55e4f9f8..a7d801c452e9ad7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2198,12 +2198,14 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
// (~X) - (~Y) --> Y - X
// This is placed after the other reassociations and explicitly excludes a
// sub-of-sub pattern to avoid infinite looping.
- if (isFreeToInvert(Op0, Op0->hasOneUse()) &&
- isFreeToInvert(Op1, Op1->hasOneUse()) &&
- !match(Op0, m_Sub(m_ImmConstant(), m_Value()))) {
- Value *NotOp0 = Builder.CreateNot(Op0);
- Value *NotOp1 = Builder.CreateNot(Op1);
- return BinaryOperator::CreateSub(NotOp1, NotOp0);
+ if (!match(Op0, m_Sub(m_ImmConstant(), m_Value())) &&
+ isFreeToInvert(Op0, Op0->hasOneUse())) {
+ if (Value *NotOp1 = getFreelyInverted(Op1, Op1->hasOneUse(), &Builder)) {
+ Value *NotOp0 = getFreelyInverted(Op0, Op0->hasOneUse(), &Builder);
+ assert(NotOp0 != nullptr &&
+ "isFreeToInvert desynced with getFreelyInverted");
+ return BinaryOperator::CreateSub(NotOp1, NotOp0);
+ }
}
auto m_AddRdx = [](Value *&Vec) {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 0750c1529d0b3c2..819bcb81f4a111d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2503,16 +2503,26 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
return BinaryOperator::CreateAnd(Op1, B);
// (A ^ B) & ((B ^ C) ^ A) -> (A ^ B) & ~C
- if (match(Op0, m_Xor(m_Value(A), m_Value(B))))
- if (match(Op1, m_Xor(m_Xor(m_Specific(B), m_Value(C)), m_Specific(A))))
- if (Op1->hasOneUse() || isFreeToInvert(C, C->hasOneUse()))
- return BinaryOperator::CreateAnd(Op0, Builder.CreateNot(C));
+ if (match(Op0, m_Xor(m_Value(A), m_Value(B)))) {
+ if (match(Op1, m_Xor(m_Xor(m_Specific(B), m_Value(C)), m_Specific(A)))) {
+ Value *NotC = Op1->hasOneUse()
+ ? Builder.CreateNot(C)
+ : getFreelyInverted(C, C->hasOneUse(), &Builder);
+ if (NotC != nullptr)
+ return BinaryOperator::CreateAnd(Op0, NotC);
+ }
+ }
// ((A ^ C) ^ B) & (B ^ A) -> (B ^ A) & ~C
- if (match(Op0, m_Xor(m_Xor(m_Value(A), m_Value(C)), m_Value(B))))
- if (match(Op1, m_Xor(m_Specific(B), m_Specific(A))))
- if (Op0->hasOneUse() || isFreeToInvert(C, C->hasOneUse()))
+ if (match(Op0, m_Xor(m_Xor(m_Value(A), m_Value(C)), m_Value(B)))) {
+ if (match(Op1, m_Xor(m_Specific(B), m_Specific(A)))) {
+ Value *NotC = Op0->hasOneUse()
+ ? Builder.CreateNot(C)
+ : getFreelyInverted(C, C->hasOneUse(), &Builder);
+ if (NotC != nullptr)
return BinaryOperator::CreateAnd(Op1, Builder.CreateNot(C));
+ }
+ }
// (A | B) & (~A ^ B) -> A & B
// (A | B) & (B ^ ~A) -> A & B
@@ -3997,14 +4007,14 @@ static Instruction *visitMaskedMerge(BinaryOperator &I,
static Instruction *sinkNotIntoXor(BinaryOperator &I, Value *X, Value *Y,
InstCombiner::BuilderTy &Builder) {
// We only want to do the transform if it is free to do.
- if (InstCombiner::isFreeToInvert(X, X->hasOneUse())) {
- // Ok, good.
- } else if (InstCombiner::isFreeToInvert(Y, Y->hasOneUse())) {
+ Value *NotX = InstCombiner::getFreelyInverted(X, X->hasOneUse(), &Builder);
+ if (NotX == nullptr) {
std::swap(X, Y);
- } else
+ NotX = InstCombiner::getFreelyInverted(X, X->hasOneUse(), &Builder);
+ }
+ if (NotX == nullptr)
return nullptr;
- Value *NotX = Builder.CreateNot(X, X->getName() + ".not");
return BinaryOperator::CreateXor(NotX, Y, I.getName() + ".demorgan");
}
@@ -4317,13 +4327,15 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
auto *II = dyn_cast<IntrinsicInst>(NotOp);
if (II && II->hasOneUse()) {
if (match(NotOp, m_MaxOrMin(m_Value(X), m_Value(Y))) &&
- isFreeToInvert(X, X->hasOneUse()) &&
- isFreeToInvert(Y, Y->hasOneUse())) {
- Intrinsic::ID InvID = getInverseMinMaxIntrinsic(II->getIntrinsicID());
- Value *NotX = Builder.CreateNot(X);
- Value *NotY = Builder.CreateNot(Y);
- Value *InvMaxMin = Builder.CreateBinaryIntrinsic(InvID, NotX, NotY);
- return replaceInstUsesWith(I, InvMaxMin);
+ isFreeToInvert(X, X->hasOneUse())) {
+ if (Value *NotY = getFreelyInverted(Y, Y->hasOneUse(), &Builder)) {
+ Intrinsic::ID InvID = getInverseMinMaxIntrinsic(II->getIntrinsicID());
+ Value *NotX = getFreelyInverted(X, X->hasOneUse(), &Builder);
+ assert(NotX != nullptr &&
+ "isFreeToInvert desynced with getFreelyInverted");
+ Value *InvMaxMin = Builder.CreateBinaryIntrinsic(InvID, NotX, NotY);
+ return replaceInstUsesWith(I, InvMaxMin);
+ }
}
if (match(NotOp, m_c_MaxOrMin(m_Not(m_Value(X)), m_Value(Y)))) {
Intrinsic::ID InvID = getInverseMinMaxIntrinsic(II->getIntrinsicID());
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 64cbfebf0102876..f8346cd03849acf 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1698,12 +1698,12 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
auto moveNotAfterMinMax = [&](Value *X, Value *Y) -> Instruction * {
Value *A;
if (match(X, m_OneUse(m_Not(m_Value(A)))) &&
- !isFreeToInvert(A, A->hasOneUse()) &&
- isFreeToInvert(Y, Y->hasOneUse())) {
- Value *NotY = Builder.CreateNot(Y);
- Intrinsic::ID InvID = getInverseMinMaxIntrinsic(IID);
- Value *InvMaxMin = Builder.CreateBinaryIntrinsic(InvID, A, NotY);
- return BinaryOperator::CreateNot(InvMaxMin);
+ !isFreeToInvert(A, A->hasOneUse())) {
+ if (Value *NotY = getFreelyInverted(Y, Y->hasOneUse(), &Builder)) {
+ Intrinsic::ID InvID = getInverseMinMaxIntrinsic(IID);
+ Value *InvMaxMin = Builder.CreateBinaryIntrinsic(InvID, A, NotY);
+ return BinaryOperator::CreateNot(InvMaxMin);
+ }
}
return nullptr;
};
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 9bc84c7dd6e1539..74865c487c2b2d2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3226,10 +3226,12 @@ Instruction *InstCombinerImpl::foldICmpBitCast(ICmpInst &Cmp) {
// icmp eq/ne (bitcast (not X) to iN), -1 --> icmp eq/ne (bitcast X to iN), 0
// Example: are all elements equal? --> are zero elements not equal?
// TODO: Try harder to reduce compare of 2 freely invertible operands?
- if (Cmp.isEquality() && C->isAllOnes() && Bitcast->hasOneUse() &&
- isFreeToInvert(BCSrcOp, BCSrcOp->hasOneUse())) {
- Value *Cast = Builder.CreateBitCast(Builder.CreateNot(BCSrcOp), DstType);
- return new ICmpInst(Pred, Cast, ConstantInt::getNullValue(DstType));
+ if (Cmp.isEquality() && C->isAllOnes() && Bitcast->hasOneUse()) {
+ if (Value *NotBCSrcOp =
+ getFreelyInverted(BCSrcOp, BCSrcOp->hasOneUse(), &Builder)) {
+ Value *Cast = Builder.CreateBitCast(NotBCSrcOp, DstType);
+ return new ICmpInst(Pred, Cast, ConstantInt::getNullValue(DstType));
+ }
}
// If this is checking if all elements of an extended vector are clear or not,
@@ -4494,14 +4496,13 @@ static Instruction *foldICmpOrXX(ICmpInst &I, const SimplifyQuery &Q,
if (ICmpInst::isEquality(Pred) && Op0->hasOneUse()) {
// icmp (X | Y) eq/ne Y --> (X & ~Y) eq/ne 0 if Y is freely invertible
- if (IC.isFreeToInvert(Op1, Op1->hasOneUse()))
- return new ICmpInst(Pred,
- IC.Builder.CreateAnd(A, IC.Builder.CreateNot(Op1)),
+ if (Value *NotOp1 =
+ IC.getFreelyInverted(Op1, Op1->hasOneUse(), &IC.Builder))
+ return new ICmpInst(Pred, IC.Builder.CreateAnd(A, NotOp1),
Constant::getNullValue(Op1->getType()));
// icmp (X | Y) eq/ne Y --> (~X | Y) eq/ne -1 if X is freely invertible.
- if (IC.isFreeToInvert(A, A->hasOneUse()))
- return new ICmpInst(Pred,
- IC.Builder.CreateOr(Op1, IC.Builder.CreateNot(A)),
+ if (Value *NotA = IC.getFreelyInverted(A, A->hasOneUse(), &IC.Builder))
+ return new ICmpInst(Pred, IC.Builder.CreateOr(Op1, NotA),
Constant::getAllOnesValue(Op1->getType()));
}
return nullptr;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 71c2d68881441ac..2dda46986f0fd08 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3075,18 +3075,18 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
return SelectInst::Create(TrueVal, OrV, Zero);
}
// select (c & b), a, b -> select b, (select ~c, true, a), false
- if (match(CondVal, m_OneUse(m_c_And(m_Value(C), m_Specific(FalseVal)))) &&
- isFreeToInvert(C, C->hasOneUse())) {
- Value *NotC = Builder.CreateNot(C);
- Value *OrV = Builder.CreateSelect(NotC, One, TrueVal);
- return SelectInst::Create(FalseVal, OrV, Zero);
+ if (match(CondVal, m_OneUse(m_c_And(m_Value(C), m_Specific(FalseVal))))) {
+ if (Value *NotC = getFreelyInverted(C, C->hasOneUse(), &Builder)) {
+ Value *OrV = Builder.CreateSelect(NotC, One, TrueVal);
+ return SelectInst::Create(FalseVal, OrV, Zero);
+ }
}
// select (a | c), a, b -> select a, true, (select ~c, b, false)
- if (match(CondVal, m_OneUse(m_c_Or(m_Specific(TrueVal), m_Value(C)))) &&
- isFreeToInvert(C, C->hasOneUse())) {
- Value *NotC = Builder.CreateNot(C);
- Value *AndV = Builder.CreateSelect(NotC, FalseVal, Zero);
- return SelectInst::Create(TrueVal, One, AndV);
+ if (match(CondVal, m_OneUse(m_c_Or(m_Specific(TrueVal), m_Value(C))))) {
+ if (Value *NotC = getFreelyInverted(C, C->hasOneUse(), &Builder)) {
+ Value *AndV = Builder.CreateSelect(NotC, FalseVal, Zero);
+ return SelectInst::Create(TrueVal, One, AndV);
+ }
}
// select (c & ~b), a, b -> select b, true, (select c, a, false)
if (match(CondVal,
diff --git a/llvm/test/Transforms/InstCombine/free-inversion.ll b/llvm/test/Transforms/InstCombine/free-inversion.ll
index acafc1f4c4048a5..4e5eef2b69b4dc0 100644
--- a/llvm/test/Transforms/InstCombine/free-inversion.ll
+++ b/llvm/test/Transforms/InstCombine/free-inversion.ll
@@ -11,8 +11,8 @@ declare void @use.i8(i8)
define i8 @xor_1(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @xor_1(
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
-; CHECK-NEXT: [[B_NOT:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
-; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[B_NOT]], [[A:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_BA:%.*]] = xor i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_BA]]
;
%nx = xor i8 %x, -1
@@ -26,8 +26,8 @@ define i8 @xor_1(i8 %a, i1 %c, i8 %x, i8 %y) {
define i8 @xor_2(i8 %a, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @xor_2(
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
-; CHECK-NEXT: [[B_NOT:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[B_NOT]], [[A:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = xor i8 [[TMP2]], [[A:%.*]]
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -253,10 +253,10 @@ define i1 @select_logic_and_fail(i1 %cc, i1 %c, i1 %x, i8 %y) {
define i8 @smin_1(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @smin_1(
-; CHECK-NEXT: [[TMP1:%.*]] = sub i8 [[NA:%.*]], [[AA:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y:%.*]], -124
-; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP2]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[TMP3]])
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = sub i8 [[NA:%.*]], [[AA:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP3]], i8 [[TMP2]])
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
@@ -303,10 +303,10 @@ define i8 @umin_1_fail(i1 %c, i8 %x, i8 %y) {
define i8 @smax_1(i8 %aa, i8 %na, i1 %c, i8 %x, i8 %y) {
; CHECK-LABEL: @smax_1(
-; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[NA:%.*]], [[AA:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[Y:%.*]], -124
-; CHECK-NEXT: [[TMP3:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP2]]
-; CHECK-NEXT: [[NOT_AB:%.*]] = call i8 @llvm.smin.i8(i8 [[TMP1]], i8 [[TMP3]])
+; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -124
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[C:%.*]], i8 [[X:%.*]], i8 [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = add i8 [[NA:%.*]], [[AA:%.*]]
+; CHECK-NEXT: [[NOT_AB:%.*]] = call i8 @llvm.smin.i8(i8 [[TMP3]], i8 [[TMP2]])
; CHECK-NEXT: ret i8 [[NOT_AB]]
;
%nx = xor i8 %x, -1
diff --git a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
index 6df9bb06ca56d2e..404bfda39d44abf 100644
--- a/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
+++ b/llvm/test/Transforms/InstCombine/minmax-intrinsics.ll
@@ -1371,9 +1371,9 @@ define i8 @freeToInvert_two_minmax_ops(i8 %x, i8 %y, i8 %z, i8 %w) {
; CHECK-NEXT: call void @use(i8 [[NY]])
; CHECK-NEXT: call void @use(i8 [[NZ]])
; CHECK-NEXT: call void @use(i8 [[NW]])
-; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
-; CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.smin.i8(i8 [[W]], i8 [[Z]])
-; CHECK-NEXT: [[NOT:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP1]], i8 [[TMP2]])
+; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.smin.i8(i8 [[W]], i8 [[Z]])
+; CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.umin.i8(i8 [[X]], i8 [[Y]])
+; CHECK-NEXT: [[NOT:%.*]] = call i8 @llvm.smax.i8(i8 [[TMP2]], i8 [[TMP1]])
; CHECK-NEXT: ret i8 [[NOT]]
;
%nx = xor i8 %x, -1
diff --git a/llvm/test/Transforms/InstCombine/pr38915.ll b/llvm/test/Transforms/InstCombine/pr38915.ll
index 0874fe6cc927d9c..c375f9001029a2c 100644
--- a/llvm/test/Transforms/InstCombine/pr38915.ll
+++ b/llvm/test/Transforms/InstCombine/pr38915.ll
@@ -3,9 +3,9 @@
define i32 @PR38915(i32 %x, i32 %y, i32 %z) {
; CHECK-LABEL: @PR38915(
-; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[Y:%.*]], -1
-; CHECK-NEXT: [[M1N:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP1]], i32 [[TMP2]])
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[Y:%.*]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[X:%.*]], -1
+; CHECK-NEXT: [[M1N:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP2]], i32 [[TMP1]])
; CHECK-NEXT: [[M2:%.*]] = call i32 @llvm.smax.i32(i32 [[M1N]], i32 [[Z:%.*]])
; CHECK-NEXT: [[M2N:%.*]] = xor i32 [[M2]], -1
; CHECK-NEXT: ret i32 [[M2N]]
diff --git a/llvm/test/Transforms/InstCombine/xor.ll b/llvm/test/Transforms/InstCombine/xor.ll
index d4b49afe9efe6fc..ff3fca66c0a8381 100644
--- a/llvm/test/Transforms/InstCombine/xor.ll
+++ b/llvm/test/Transforms/InstCombine/xor.ll
@@ -842,9 +842,9 @@ define <2 x i32> @test49vec(<2 x i32> %x) {
define i32 @test50(i32 %x, i32 %y) {
; CHECK-LABEL: @test50(
-; CHECK-NEXT: [[TMP1:%.*]] = sub i32 1, [[X:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[Y:%.*]], 1
-; CHECK-NEXT: [[E:%.*]] = call i32 @llvm.smax.i32(i32 [[TMP1]], i32 [[TMP2]])
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[Y:%.*]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = sub i32 1, [[X:%.*]]
+; CHECK-NEXT: [[E:%.*]] = call i32 @llvm.smax.i32(i32 [[TMP2]], i32 [[TMP1]])
; CHECK-NEXT: ret i32 [[E]]
;
%a = add i32 %x, -2
@@ -857,9 +857,9 @@ define i32 @test50(i32 %x, i32 %y) {
define <2 x i32> @test50vec(<2 x i32> %x, <2 x i32> %y) {
; CHECK-LABEL: @test50vec(
-; CHECK-NEXT: [[TMP1:%.*]] = sub <2 x i32> <i32 1, i32 1>, [[X:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = add <2 x i32> [[Y:%.*]], <i32 1, i32 1>
-; CHECK-NEXT: [[E:%.*]] = call <2 x i32> @llvm.smax.v2i32(<2 x i32> [[TMP1]], <2 x i32> [[TMP2]])
+; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[Y:%.*]], <i32 1, i32 1>
+; CHECK-NEXT: [[TMP2:%.*]] = sub <2 x i32> <i32 1, i32 1>, [[X:%.*]]
+; CHECK-NEXT: [[E:%.*]] = call <2 x i32> @llvm.smax.v2i32(<2 x i32> [[TMP2]], <2 x i32> [[TMP1]])
; CHECK-NEXT: ret <2 x i32> [[E]]
;
%a = add <2 x i32> %x, <i32 -2, i32 -2>
@@ -872,9 +872,9 @@ define <2 x i32> @test50vec(<2 x i32> %x, <2 x i32> %y) {
define i32 @test51(i32 %x, i32 %y) {
; CHECK-LABEL: @test51(
-; CHECK-NEXT: [[TMP1:%.*]] = sub i32 -3, [[X:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[Y:%.*]], -3
-; CHECK-NEXT: [[E:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP1]], i32 [[TMP2]])
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[Y:%.*]], -3
+; CHECK-NEXT: [[TMP2:%.*]] = sub i32 -3, [[X:%.*]]
+; CHECK-NEXT: [[E:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP2]], i32 [[TMP1]])
; CHECK-NEXT: ret i32 [[E]]
;
%a = add i32 %x, 2
@@ -887,9 +887,9 @@ define i32 @test51(i32 %x, i32 %y) {
define <2 x i32> @test51vec(<2 x i32> %x, <2 x i32> %y) {
; CHECK-LABEL: @test51vec(
-; CHECK-NEXT: [[TMP1:%.*]] = sub <2 x i32> <i32 -3, i32 -3>, [[X:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = add <2 x i32> [[Y:%.*]], <i32 -3, i32 -3>
-; CHECK-NEXT: [[E:%.*]] = call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[TMP1]], <2 x i32> [[TMP2]])
+; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[Y:%.*]], <i32 -3, i32 -3>
+; CHECK-NEXT: [[TMP2:%.*]] = sub <2 x i32> <i32 -3, i32 -3>, [[X:%.*]]
+; CHECK-NEXT: [[E:%.*]] = call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[TMP2]], <2 x i32> [[TMP1]])
; CHECK-NEXT: ret <2 x i32> [[E]]
;
%a = add <2 x i32> %x, <i32 2, i32 2>
More information about the llvm-commits
mailing list