diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -3219,6 +3219,11 @@ // ~(X + C) --> -(C + 1) - X if (match(Op0, m_Add(m_Value(X), m_Constant(C)))) return BinaryOperator::CreateSub(ConstantExpr::getNeg(AddOne(C)), X); + + // ~(~X + Y) --> X - Y + if (match(NotVal, m_c_Add(m_Not(m_Value(X)), m_Value(Y)))) + return BinaryOperator::CreateWithCopiedFlags(Instruction::Sub, X, Y, + NotVal); } // Use DeMorgan and reassociation to eliminate a 'not' op. diff --git a/llvm/test/Transforms/InstCombine/not-add.ll b/llvm/test/Transforms/InstCombine/not-add.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/not-add.ll @@ -0,0 +1,155 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -instcombine -S < %s | FileCheck %s + +declare void @use(i8) +declare i8 @get() + +define i8 @basic(i8 %x, i8 %y) { +; CHECK-LABEL: @basic( +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + %a = add i8 %notx, %y + %nota = xor i8 %a, -1 + ret i8 %nota +} + + +define i8 @basic_commuted(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_commuted( +; CHECK-NEXT: [[NOTX:%.*]] = xor i8 [[X:%.*]], -1 +; CHECK-NEXT: [[A:%.*]] = add i8 [[NOTX]], [[Y:%.*]] +; CHECK-NEXT: call void @use(i8 [[A]]) +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[X]], [[Y]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + %a = add i8 %y, %notx + call void @use(i8 %a) + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_commuted2(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_commuted2( +; CHECK-NEXT: [[NOTY:%.*]] = xor i8 [[Y:%.*]], -1 +; CHECK-NEXT: [[A:%.*]] = add i8 [[NOTY]], [[X:%.*]] +; CHECK-NEXT: call void @use(i8 [[A]]) +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[Y]], [[X]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %noty = xor i8 %y, -1 + %a = add i8 %noty, %x + call void @use(i8 %a) + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_commuted3(i8 %y) { +; CHECK-LABEL: @basic_commuted3( +; CHECK-NEXT: [[X:%.*]] = call i8 @get() +; CHECK-NEXT: [[NOTY:%.*]] = xor i8 [[Y:%.*]], -1 +; CHECK-NEXT: [[A:%.*]] = add i8 [[X]], [[NOTY]] +; CHECK-NEXT: call void @use(i8 [[A]]) +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[Y]], [[X]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %x = call i8 @get() + %noty = xor i8 %y, -1 + %a = add i8 %x, %noty + call void @use(i8 %a) + %nota = xor i8 %a, -1 + ret i8 %nota +} + + +define i8 @basic_use(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_use( +; CHECK-NEXT: [[NOTX:%.*]] = xor i8 [[X:%.*]], -1 +; CHECK-NEXT: call void @use(i8 [[NOTX]]) +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[X]], [[Y:%.*]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + call void @use(i8 %notx) + %a = add i8 %notx, %y + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_use2(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_use2( +; CHECK-NEXT: [[NOTX:%.*]] = xor i8 [[X:%.*]], -1 +; CHECK-NEXT: [[A:%.*]] = add i8 [[NOTX]], [[Y:%.*]] +; CHECK-NEXT: call void @use(i8 [[A]]) +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[X]], [[Y]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + %a = add i8 %notx, %y + call void @use(i8 %a) + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_use_both(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_use_both( +; CHECK-NEXT: [[NOTX:%.*]] = xor i8 [[X:%.*]], -1 +; CHECK-NEXT: call void @use(i8 [[NOTX]]) +; CHECK-NEXT: [[A:%.*]] = add i8 [[NOTX]], [[Y:%.*]] +; CHECK-NEXT: call void @use(i8 [[A]]) +; CHECK-NEXT: [[NOTA:%.*]] = sub i8 [[X]], [[Y]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + call void @use(i8 %notx) + %a = add i8 %notx, %y + call void @use(i8 %a) + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_preserve_nsw(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_preserve_nsw( +; CHECK-NEXT: [[NOTA:%.*]] = sub nsw i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + %a = add nsw i8 %notx, %y + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_preserve_nuw(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_preserve_nuw( +; CHECK-NEXT: [[NOTA:%.*]] = sub nuw i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + %a = add nuw i8 %notx, %y + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define i8 @basic_preserve_nuw_nsw(i8 %x, i8 %y) { +; CHECK-LABEL: @basic_preserve_nuw_nsw( +; CHECK-NEXT: [[NOTA:%.*]] = sub nuw nsw i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i8 [[NOTA]] +; + %notx = xor i8 %x, -1 + %a = add nuw nsw i8 %notx, %y + %nota = xor i8 %a, -1 + ret i8 %nota +} + +define <4 x i32> @vector_test(<4 x i32> %x, <4 x i32> %y) { +; CHECK-LABEL: @vector_test( +; CHECK-NEXT: [[NOTA:%.*]] = sub <4 x i32> [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret <4 x i32> [[NOTA]] +; + %notx = xor <4 x i32> %x, + %a = add <4 x i32> %notx, %y + %nota = xor <4 x i32> %a, + ret <4 x i32> %nota +}