Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@ namespace {
bool PromoteLoad(SDValue Op);

SDValue foldShiftToAvg(SDNode *N);
// Fold `a bitwiseop (~b +/- c)` -> `a bitwiseop ~(b -/+ c)`
SDValue foldBitwiseOpWithNeg(SDNode *N);

SDValue combineMinNumMaxNum(const SDLoc &DL, EVT VT, SDValue LHS,
SDValue RHS, SDValue True, SDValue False,
Expand Down Expand Up @@ -7528,6 +7530,11 @@ SDValue DAGCombiner::visitAND(SDNode *N) {
return DAG.getNode(ISD::AND, DL, VT, X,
DAG.getNOT(DL, DAG.getNode(Opc, DL, VT, Y, Z), VT));

// Fold (and X, (add (not Y), Z)) -> (and X, (not (sub Y, Z)))
// Fold (and X, (sub (not Y), Z)) -> (and X, (not (add Y, Z)))
if (SDValue Folded = foldBitwiseOpWithNeg(N))
return Folded;

// Fold (and (srl X, C), 1) -> (srl X, BW-1) for signbit extraction
// If we are shifting down an extended sign bit, see if we can simplify
// this to shifting the MSB directly to expose further simplifications.
Expand Down Expand Up @@ -8205,6 +8212,11 @@ SDValue DAGCombiner::visitOR(SDNode *N) {
}
}

// Fold (or X, (add (not Y), Z)) -> (or X, (not (sub Y, Z)))
// Fold (or X, (sub (not Y), Z)) -> (or X, (not (add Y, Z)))
if (SDValue Folded = foldBitwiseOpWithNeg(N))
return Folded;

// fold (or x, 0) -> x
if (isNullConstant(N1))
return N0;
Expand Down Expand Up @@ -9856,6 +9868,10 @@ SDValue DAGCombiner::visitXOR(SDNode *N) {
return DAG.getNode(ISD::ROTL, DL, VT, DAG.getSignedConstant(~1, DL, VT),
N0.getOperand(1));
}
// Fold (xor X, (add (not Y), Z)) -> (xor X, (not (sub Y, Z)))
// Fold (xor X, (sub (not Y), Z)) -> (xor X, (not (add Y, Z)))
if (SDValue Folded = foldBitwiseOpWithNeg(N))
return Folded;

// Simplify: xor (op x...), (op y...) -> (op (xor x, y))
if (N0Opcode == N1.getOpcode())
Expand Down Expand Up @@ -11609,6 +11625,35 @@ SDValue DAGCombiner::foldShiftToAvg(SDNode *N) {
return DAG.getNode(FloorISD, SDLoc(N), N->getValueType(0), {A, B});
}

SDValue DAGCombiner::foldBitwiseOpWithNeg(SDNode *N) {
if (!TLI.hasAndNot(SDValue(N, 0)))
return SDValue();

unsigned Opc = N->getOpcode();
if (Opc != ISD::AND && Opc != ISD::OR && Opc != ISD::XOR)
return SDValue();

SDValue N1 = N->getOperand(1);
EVT VT = N1.getValueType();
SDLoc DL(N);
SDValue X, Y, Z, NotY;

if (sd_match(N, m_c_BinOp(Opc, m_Value(X),
m_Add(m_AllOf(m_Value(NotY), m_Not(m_Value(Y))),
m_Value(Z)))))
return DAG.getNode(Opc, DL, VT, X,
DAG.getNOT(DL, DAG.getNode(ISD::SUB, DL, VT, Y, Z), VT));

if (sd_match(N, m_c_BinOp(Opc, m_Value(X),
m_Sub(m_AllOf(m_Value(NotY), m_Not(m_Value(Y))),
m_Value(Z)))) &&
NotY->hasOneUse())
return DAG.getNode(Opc, DL, VT, X,
DAG.getNOT(DL, DAG.getNode(ISD::ADD, DL, VT, Y, Z), VT));

return SDValue();
}

/// Generate Min/Max node
SDValue DAGCombiner::combineMinNumMaxNum(const SDLoc &DL, EVT VT, SDValue LHS,
SDValue RHS, SDValue True,
Expand Down
316 changes: 316 additions & 0 deletions llvm/test/CodeGen/AArch64/aarch64-bitwisenot-fold.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: llc < %s -mtriple=aarch64-linux | FileCheck %s

define i8 @andnot_add_with_neg_i8(i8 %0, i8 %1) {
; CHECK-LABEL: andnot_add_with_neg_i8:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: bic w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i8 %0, -1
%4 = add i8 %3, %1
%5 = and i8 %4, %0
ret i8 %5
}

define i8 @andnot_sub_with_neg_i8(i8 %0, i8 %1) {
; CHECK-LABEL: andnot_sub_with_neg_i8:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: bic w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i8 %0, -1
%4 = sub i8 %3, %1
%5 = and i8 %4, %0
ret i8 %5
}

define i8 @xornot_add_with_neg_i8(i8 %0, i8 %1) {
; CHECK-LABEL: xornot_add_with_neg_i8:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: eon w0, w8, w0
; CHECK-NEXT: ret
entry:
%3 = xor i8 %0, -1
%4 = add i8 %3, %1
%5 = xor i8 %4, %0
ret i8 %5
}

define i8 @xornot_sub_with_neg_i8(i8 %0, i8 %1) {
; CHECK-LABEL: xornot_sub_with_neg_i8:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: eon w0, w8, w0
; CHECK-NEXT: ret
entry:
%3 = xor i8 %0, -1
%4 = sub i8 %3, %1
%5 = xor i8 %4, %0
ret i8 %5
}

define i8 @ornot_add_with_neg_i8(i8 %0, i8 %1) {
; CHECK-LABEL: ornot_add_with_neg_i8:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: orn w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i8 %0, -1
%4 = add i8 %3, %1
%5 = or i8 %4, %0
ret i8 %5
}

define i8 @ornot_sub_with_neg_i8(i8 %0, i8 %1) {
; CHECK-LABEL: ornot_sub_with_neg_i8:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: orn w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i8 %0, -1
%4 = sub i8 %3, %1
%5 = or i8 %4, %0
ret i8 %5
}


define i16 @andnot_add_with_neg_i16(i16 %0, i16 %1) {
; CHECK-LABEL: andnot_add_with_neg_i16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: bic w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i16 %0, -1
%4 = add i16 %3, %1
%5 = and i16 %4, %0
ret i16 %5
}

define i16 @andnot_sub_with_neg_i16(i16 %0, i16 %1) {
; CHECK-LABEL: andnot_sub_with_neg_i16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: bic w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i16 %0, -1
%4 = sub i16 %3, %1
%5 = and i16 %4, %0
ret i16 %5
}

define i16 @xornot_add_with_neg_i16(i16 %0, i16 %1) {
; CHECK-LABEL: xornot_add_with_neg_i16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: eon w0, w8, w0
; CHECK-NEXT: ret
entry:
%3 = xor i16 %0, -1
%4 = add i16 %3, %1
%5 = xor i16 %4, %0
ret i16 %5
}

define i16 @xornot_sub_with_neg_i16(i16 %0, i16 %1) {
; CHECK-LABEL: xornot_sub_with_neg_i16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: eon w0, w8, w0
; CHECK-NEXT: ret
entry:
%3 = xor i16 %0, -1
%4 = sub i16 %3, %1
%5 = xor i16 %4, %0
ret i16 %5
}

define i16 @ornot_add_with_neg_i16(i16 %0, i16 %1) {
; CHECK-LABEL: ornot_add_with_neg_i16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: orn w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i16 %0, -1
%4 = add i16 %3, %1
%5 = or i16 %4, %0
ret i16 %5
}

define i16 @ornot_sub_with_neg_i16(i16 %0, i16 %1) {
; CHECK-LABEL: ornot_sub_with_neg_i16:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: orn w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i16 %0, -1
%4 = sub i16 %3, %1
%5 = or i16 %4, %0
ret i16 %5
}

define i32 @andnot_add_with_neg_i32(i32 %0, i32 %1) {
; CHECK-LABEL: andnot_add_with_neg_i32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: bic w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i32 %0, -1
%4 = add i32 %3, %1
%5 = and i32 %4, %0
ret i32 %5
}

define i32 @andnot_sub_with_neg_i32(i32 %0, i32 %1) {
; CHECK-LABEL: andnot_sub_with_neg_i32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: bic w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i32 %0, -1
%4 = sub i32 %3, %1
%5 = and i32 %4, %0
ret i32 %5
}

define i32 @xornot_add_with_neg_i32(i32 %0, i32 %1) {
; CHECK-LABEL: xornot_add_with_neg_i32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: eon w0, w8, w0
; CHECK-NEXT: ret
entry:
%3 = xor i32 %0, -1
%4 = add i32 %3, %1
%5 = xor i32 %4, %0
ret i32 %5
}

define i32 @xornot_sub_with_neg_i32(i32 %0, i32 %1) {
; CHECK-LABEL: xornot_sub_with_neg_i32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: eon w0, w8, w0
; CHECK-NEXT: ret
entry:
%3 = xor i32 %0, -1
%4 = sub i32 %3, %1
%5 = xor i32 %4, %0
ret i32 %5
}

define i32 @ornot_add_with_neg_i32(i32 %0, i32 %1) {
; CHECK-LABEL: ornot_add_with_neg_i32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub w8, w0, w1
; CHECK-NEXT: orn w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i32 %0, -1
%4 = add i32 %3, %1
%5 = or i32 %4, %0
ret i32 %5
}

define i32 @ornot_sub_with_neg_i32(i32 %0, i32 %1) {
; CHECK-LABEL: ornot_sub_with_neg_i32:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add w8, w0, w1
; CHECK-NEXT: orn w0, w0, w8
; CHECK-NEXT: ret
entry:
%3 = xor i32 %0, -1
%4 = sub i32 %3, %1
%5 = or i32 %4, %0
ret i32 %5
}


define i64 @andnot_add_with_neg_i64(i64 %0, i64 %1) {
; CHECK-LABEL: andnot_add_with_neg_i64:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub x8, x0, x1
; CHECK-NEXT: bic x0, x0, x8
; CHECK-NEXT: ret
entry:
%3 = xor i64 %0, -1
%4 = add i64 %3, %1
%5 = and i64 %4, %0
ret i64 %5
}

define i64 @andnot_sub_with_neg_i64(i64 %0, i64 %1) {
; CHECK-LABEL: andnot_sub_with_neg_i64:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add x8, x0, x1
; CHECK-NEXT: bic x0, x0, x8
; CHECK-NEXT: ret
entry:
%3 = xor i64 %0, -1
%4 = sub i64 %3, %1
%5 = and i64 %4, %0
ret i64 %5
}

define i64 @xornot_add_with_neg_i64(i64 %0, i64 %1) {
; CHECK-LABEL: xornot_add_with_neg_i64:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub x8, x0, x1
; CHECK-NEXT: eon x0, x8, x0
; CHECK-NEXT: ret
entry:
%3 = xor i64 %0, -1
%4 = add i64 %3, %1
%5 = xor i64 %4, %0
ret i64 %5
}

define i64 @xornot_sub_with_neg_i64(i64 %0, i64 %1) {
; CHECK-LABEL: xornot_sub_with_neg_i64:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add x8, x0, x1
; CHECK-NEXT: eon x0, x8, x0
; CHECK-NEXT: ret
entry:
%3 = xor i64 %0, -1
%4 = sub i64 %3, %1
%5 = xor i64 %4, %0
ret i64 %5
}

define i64 @ornot_add_with_neg_i64(i64 %0, i64 %1) {
; CHECK-LABEL: ornot_add_with_neg_i64:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: sub x8, x0, x1
; CHECK-NEXT: orn x0, x0, x8
; CHECK-NEXT: ret
entry:
%3 = xor i64 %0, -1
%4 = add i64 %3, %1
%5 = or i64 %4, %0
ret i64 %5
}

define i64 @ornot_sub_with_neg_i64(i64 %0, i64 %1) {
; CHECK-LABEL: ornot_sub_with_neg_i64:
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: add x8, x0, x1
; CHECK-NEXT: orn x0, x0, x8
; CHECK-NEXT: ret
entry:
%3 = xor i64 %0, -1
%4 = sub i64 %3, %1
%5 = or i64 %4, %0
ret i64 %5
}
Loading