From 166c1849d6da1577b49305371db1cdfacbb150e4 Mon Sep 17 00:00:00 2001 From: Noah Goldstein Date: Mon, 1 Apr 2024 13:46:30 -0500 Subject: [PATCH] [InstCombine] Fold `(icmp eq/ne (xor x, y), C1)` even if multiuse Two folds unlocked: `(icmp eq/ne (xor x, C0), C1)` -> `(icmp eq/ne x, C2)` `(icmp eq/ne (xor x, y), 0)` -> `(icmp eq/ne x, y)` This fixes regressions assosiated with #87180 Closes #87275 --- .../InstCombine/InstCombineCompares.cpp | 16 +++++++--------- .../Transforms/InstCombine/icmp-equality-xor.ll | 4 ++-- llvm/test/Transforms/InstCombine/icmp-or.ll | 16 ++++++++++------ .../Transforms/InstCombine/prevent-cmp-merge.ll | 14 +++++++------- llvm/test/Transforms/InstCombine/select.ll | 4 ++-- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 38c1c26445540e..4203147bc6a540 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -3502,15 +3502,13 @@ Instruction *InstCombinerImpl::foldICmpBinOpEqualityWithConstant( break; } case Instruction::Xor: - if (BO->hasOneUse()) { - if (Constant *BOC = dyn_cast(BOp1)) { - // For the xor case, we can xor two constants together, eliminating - // the explicit xor. - return new ICmpInst(Pred, BOp0, ConstantExpr::getXor(RHS, BOC)); - } else if (C.isZero()) { - // Replace ((xor A, B) != 0) with (A != B) - return new ICmpInst(Pred, BOp0, BOp1); - } + if (Constant *BOC = dyn_cast(BOp1)) { + // For the xor case, we can xor two constants together, eliminating + // the explicit xor. + return new ICmpInst(Pred, BOp0, ConstantExpr::getXor(RHS, BOC)); + } else if (C.isZero()) { + // Replace ((xor A, B) != 0) with (A != B) + return new ICmpInst(Pred, BOp0, BOp1); } break; case Instruction::Or: { diff --git a/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll b/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll index 91282b4d6c3312..e8a78df6d5f756 100644 --- a/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll +++ b/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll @@ -150,7 +150,7 @@ declare void @use.i8(i8) define i1 @fold_xorC_eq0_multiuse(i8 %x, i8 %y) { ; CHECK-LABEL: @fold_xorC_eq0_multiuse( ; CHECK-NEXT: [[XX:%.*]] = xor i8 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[XX]], 0 +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X]], [[Y]] ; CHECK-NEXT: call void @use.i8(i8 [[XX]]) ; CHECK-NEXT: ret i1 [[R]] ; @@ -176,7 +176,7 @@ define i1 @fold_xorC_eq1_multiuse_fail(i8 %x, i8 %y) { define i1 @fold_xorC_neC_multiuse(i8 %x) { ; CHECK-LABEL: @fold_xorC_neC_multiuse( ; CHECK-NEXT: [[XX:%.*]] = xor i8 [[X:%.*]], 45 -; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[XX]], 67 +; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[X]], 110 ; CHECK-NEXT: call void @use.i8(i8 [[XX]]) ; CHECK-NEXT: ret i1 [[R]] ; diff --git a/llvm/test/Transforms/InstCombine/icmp-or.ll b/llvm/test/Transforms/InstCombine/icmp-or.ll index 1f9db5e5db9aad..bedaf591fb070e 100644 --- a/llvm/test/Transforms/InstCombine/icmp-or.ll +++ b/llvm/test/Transforms/InstCombine/icmp-or.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; RUN: opt < %s -passes='instcombine' -S | FileCheck %s declare void @use(i8) @@ -428,13 +428,17 @@ define i1 @icmp_or_xor_2_ne_fail(i64 %x1, i64 %y1, i64 %x2, i64 %y2) { ; negative test - xor multiuse +; NB: This requires more than 1 iteration to simplify. After we +; simplify `%cmp_1 = icmp eq i64 %xor, 0`, `%xor = xor i64 %x1, %y1` +; has one use which allows for complete simplification (rooted on +; `%or1 = or i1 %cmp, %cmp_1` so we don't end up adding it back). define i1 @icmp_or_xor_2_3_fail(i64 %x1, i64 %y1, i64 %x2, i64 %y2) { ; CHECK-LABEL: @icmp_or_xor_2_3_fail( ; CHECK-NEXT: [[XOR:%.*]] = xor i64 [[X1:%.*]], [[Y1:%.*]] ; CHECK-NEXT: [[XOR1:%.*]] = xor i64 [[X2:%.*]], [[Y2:%.*]] ; CHECK-NEXT: [[OR:%.*]] = or i64 [[XOR]], [[XOR1]] ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[OR]], 0 -; CHECK-NEXT: [[CMP_1:%.*]] = icmp eq i64 [[XOR]], 0 +; CHECK-NEXT: [[CMP_1:%.*]] = icmp eq i64 [[X1]], [[Y1]] ; CHECK-NEXT: [[OR1:%.*]] = or i1 [[CMP]], [[CMP_1]] ; CHECK-NEXT: ret i1 [[OR1]] ; @@ -455,7 +459,7 @@ define i1 @icmp_or_xor_2_4_fail(i64 %x1, i64 %y1, i64 %x2, i64 %y2) { ; CHECK-NEXT: [[XOR1:%.*]] = xor i64 [[X2:%.*]], [[Y2:%.*]] ; CHECK-NEXT: [[OR:%.*]] = or i64 [[XOR]], [[XOR1]] ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[OR]], 0 -; CHECK-NEXT: [[CMP_1:%.*]] = icmp eq i64 [[XOR1]], 0 +; CHECK-NEXT: [[CMP_1:%.*]] = icmp eq i64 [[X2]], [[Y2]] ; CHECK-NEXT: [[OR1:%.*]] = or i1 [[CMP]], [[CMP_1]] ; CHECK-NEXT: ret i1 [[OR1]] ; @@ -955,7 +959,7 @@ define i1 @icmp_or_xor_with_sub_3_6(i64 %x1, i64 %y1, i64 %x2, i64 %y2, i64 %x3, define i1 @or_disjoint_with_constants(i8 %x) { ; CHECK-LABEL: @or_disjoint_with_constants( -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[TMP1:%.*]], 18 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 18 ; CHECK-NEXT: ret i1 [[CMP]] ; %or = or disjoint i8 %x, 1 @@ -966,8 +970,8 @@ define i1 @or_disjoint_with_constants(i8 %x) { define i1 @or_disjoint_with_constants2(i8 %x) { ; CHECK-LABEL: @or_disjoint_with_constants2( -; CHECK-NEXT: [[OR:%.*]] = or disjoint i8 [[TMP1:%.*]], 5 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[TMP1]], 66 +; CHECK-NEXT: [[OR:%.*]] = or disjoint i8 [[X:%.*]], 5 +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[X]], 66 ; CHECK-NEXT: call void @use(i8 [[OR]]) ; CHECK-NEXT: ret i1 [[CMP]] ; diff --git a/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll b/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll index cd05022b0d35da..e5906e7a969730 100644 --- a/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll +++ b/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll @@ -7,9 +7,9 @@ define zeroext i1 @test1(i32 %lhs, i32 %rhs) { ; CHECK-LABEL: @test1( -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[LHS:%.*]], 5 -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[XOR]], 10 -; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[XOR]], [[RHS:%.*]] +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[LHS:%.*]], 15 +; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[LHS]], [[RHS:%.*]] +; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[TMP1]], 5 ; CHECK-NEXT: [[SEL:%.*]] = or i1 [[CMP1]], [[CMP2]] ; CHECK-NEXT: ret i1 [[SEL]] ; @@ -23,9 +23,9 @@ define zeroext i1 @test1(i32 %lhs, i32 %rhs) { define zeroext i1 @test1_logical(i32 %lhs, i32 %rhs) { ; CHECK-LABEL: @test1_logical( -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[LHS:%.*]], 5 -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[XOR]], 10 -; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[XOR]], [[RHS:%.*]] +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[LHS:%.*]], 15 +; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[LHS]], [[RHS:%.*]] +; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[TMP1]], 5 ; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]] ; CHECK-NEXT: ret i1 [[SEL]] ; @@ -40,7 +40,7 @@ define zeroext i1 @test1_logical(i32 %lhs, i32 %rhs) { define zeroext i1 @test2(i32 %lhs, i32 %rhs) { ; CHECK-LABEL: @test2( ; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[LHS:%.*]], [[RHS:%.*]] -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[XOR]], 0 +; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[LHS]], [[RHS]] ; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[XOR]], 32 ; CHECK-NEXT: [[SEL:%.*]] = xor i1 [[CMP1]], [[CMP2]] ; CHECK-NEXT: ret i1 [[SEL]] diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll index 846ede45028e2c..b37e9175b26a5f 100644 --- a/llvm/test/Transforms/InstCombine/select.ll +++ b/llvm/test/Transforms/InstCombine/select.ll @@ -4547,7 +4547,7 @@ define i32 @src_no_trans_select_xor_eq0_xor_or(i32 %x, i32 %y) { define i32 @src_no_trans_select_xor_eq0_and_xor(i32 %x, i32 %y) { ; CHECK-LABEL: @src_no_trans_select_xor_eq0_and_xor( ; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[XOR0:%.*]] = icmp eq i32 [[XOR]], 0 +; CHECK-NEXT: [[XOR0:%.*]] = icmp eq i32 [[X]], [[Y]] ; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], [[Y]] ; CHECK-NEXT: [[COND:%.*]] = select i1 [[XOR0]], i32 [[AND]], i32 [[XOR]] ; CHECK-NEXT: ret i32 [[COND]] @@ -4563,7 +4563,7 @@ define i32 @src_no_trans_select_xor_eq0_and_xor(i32 %x, i32 %y) { define i32 @src_no_trans_select_xor_eq0_or_xor(i32 %x, i32 %y) { ; CHECK-LABEL: @src_no_trans_select_xor_eq0_or_xor( ; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[XOR0:%.*]] = icmp eq i32 [[XOR]], 0 +; CHECK-NEXT: [[XOR0:%.*]] = icmp eq i32 [[X]], [[Y]] ; CHECK-NEXT: [[OR:%.*]] = or i32 [[X]], [[Y]] ; CHECK-NEXT: [[COND:%.*]] = select i1 [[XOR0]], i32 [[OR]], i32 [[XOR]] ; CHECK-NEXT: ret i32 [[COND]]