From f245111149741e9c4e155f0fbf4a5e809f8231ed Mon Sep 17 00:00:00 2001 From: goldsteinn <35538541+goldsteinn@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:10:42 -0600 Subject: [PATCH] [InstSimpify] Simplifying `(xor (sub C_Mask, X), C_Mask)` -> `X` (#122552) - **[InstSimpify] Add tests for simplifying `(xor (sub C_Mask, X), C_Mask)`; NFC** - **[InstSimpify] Simplifying `(xor (sub C_Mask, X), C_Mask)` -> `X`** Helps address regressions with folding `clz(Pow2)`. Proof: https://alive2.llvm.org/ce/z/zGwUBp --- llvm/lib/Analysis/InstructionSimplify.cpp | 16 +++ .../InstSimplify/subnuw-with-xor.ll | 118 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 llvm/test/Transforms/InstSimplify/subnuw-with-xor.ll diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 999386c0a04917e..d69747e30f884d7 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -871,6 +871,14 @@ static Value *simplifySubInst(Value *Op0, Value *Op1, bool IsNSW, bool IsNUW, if (Value *V = simplifyByDomEq(Instruction::Sub, Op0, Op1, Q, MaxRecurse)) return V; + // (sub nuw C_Mask, (xor X, C_Mask)) -> X + if (IsNUW) { + Value *X; + if (match(Op1, m_Xor(m_Value(X), m_Specific(Op0))) && + match(Op0, m_LowBitMask())) + return X; + } + return nullptr; } @@ -2540,6 +2548,14 @@ static Value *simplifyXorInst(Value *Op0, Value *Op1, const SimplifyQuery &Q, if (Value *V = simplifyByDomEq(Instruction::Xor, Op0, Op1, Q, MaxRecurse)) return V; + // (xor (sub nuw C_Mask, X), C_Mask) -> X + { + Value *X; + if (match(Op0, m_NUWSub(m_Specific(Op1), m_Value(X))) && + match(Op1, m_LowBitMask())) + return X; + } + return nullptr; } diff --git a/llvm/test/Transforms/InstSimplify/subnuw-with-xor.ll b/llvm/test/Transforms/InstSimplify/subnuw-with-xor.ll new file mode 100644 index 000000000000000..a990e2f2ae394e8 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/subnuw-with-xor.ll @@ -0,0 +1,118 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=instsimplify -S | FileCheck %s + +define i8 @xor_w_sub_fail_missing_nuw(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @xor_w_sub_fail_missing_nuw( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X]], 15 +; CHECK-NEXT: [[R:%.*]] = sub nsw i8 15, [[XOR]] +; CHECK-NEXT: ret i8 [[R]] +; + %xor = xor i8 %x, 15 + %r = sub nsw i8 15, %xor + ret i8 %r +} + +define i8 @xor_w_sub_fail_diff_values(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @xor_w_sub_fail_diff_values( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X]], 15 +; CHECK-NEXT: [[R:%.*]] = sub nuw nsw i8 31, [[XOR]] +; CHECK-NEXT: ret i8 [[R]] +; + %xor = xor i8 %x, 15 + %r = sub nsw nuw i8 31, %xor + ret i8 %r +} + +define i8 @xor_w_sub_fail_diff_values2(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @xor_w_sub_fail_diff_values2( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X]], 31 +; CHECK-NEXT: [[R:%.*]] = sub nuw nsw i8 15, [[XOR]] +; CHECK-NEXT: ret i8 [[R]] +; + %xor = xor i8 %x, 31 + %r = sub nsw nuw i8 15, %xor + ret i8 %r +} + +define i8 @xor_w_sub_fail_not_mask(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @xor_w_sub_fail_not_mask( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X]], 30 +; CHECK-NEXT: [[R:%.*]] = sub nuw nsw i8 30, [[XOR]] +; CHECK-NEXT: ret i8 [[R]] +; + %xor = xor i8 %x, 30 + %r = sub nsw nuw i8 30, %xor + ret i8 %r +} + +define i8 @xor_w_sub_okay(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @xor_w_sub_okay( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: ret i8 [[X]] +; + %xor = xor i8 %x, 31 + %r = sub nsw nuw i8 31, %xor + ret i8 %r +} + +define i8 @sub_w_xor_fail_missing_nuw(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @sub_w_xor_fail_missing_nuw( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 15, [[X]] +; CHECK-NEXT: [[R:%.*]] = xor i8 [[SUB]], 15 +; CHECK-NEXT: ret i8 [[R]] +; + %sub = sub nsw i8 15, %x + %r = xor i8 %sub, 15 + ret i8 %r +} + +define i8 @sub_w_xor_fail_diff_values(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @sub_w_xor_fail_diff_values( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i8 15, [[X]] +; CHECK-NEXT: [[R:%.*]] = xor i8 [[SUB]], 31 +; CHECK-NEXT: ret i8 [[R]] +; + %sub = sub nsw nuw i8 15, %x + %r = xor i8 %sub, 31 + ret i8 %r +} + +define i8 @sub_w_sub_fail_diff_values2(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @sub_w_sub_fail_diff_values2( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i8 31, [[X]] +; CHECK-NEXT: [[R:%.*]] = xor i8 [[SUB]], 15 +; CHECK-NEXT: ret i8 [[R]] +; + %sub = sub nsw nuw i8 31, %x + %r = xor i8 %sub, 15 + ret i8 %r +} + +define i8 @sub_w_sub_fail_not_mask(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @sub_w_sub_fail_not_mask( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i8 30, [[X]] +; CHECK-NEXT: [[R:%.*]] = xor i8 [[SUB]], 30 +; CHECK-NEXT: ret i8 [[R]] +; + %sub = sub nsw nuw i8 30, %x + %r = xor i8 %sub, 30 + ret i8 %r +} + +define i8 @sub_w_sub_okay(i8 range(i8 0, 16) %x) { +; CHECK-LABEL: define i8 @sub_w_sub_okay( +; CHECK-SAME: i8 range(i8 0, 16) [[X:%.*]]) { +; CHECK-NEXT: ret i8 [[X]] +; + %sub = sub nsw nuw i8 31, %x + %r = xor i8 %sub, 31 + ret i8 %r +}