From caaf61eb6e17f7c82b72e239f98096b3c2cb6a9a Mon Sep 17 00:00:00 2001 From: Sergei Barannikov Date: Mon, 18 Sep 2023 14:45:41 +0300 Subject: [PATCH] [SDag] Fold saddo[_carry] with bitwise-not argument to ssubo[_carry] (#66571) Fold `(saddo (not a), 1)` to `(ssubo 0, a)` and `(saddo_carry (not a), b, c)` to `(ssubo_carry b, a, !c)`. Proof: https://alive2.llvm.org/ce/z/Lj49YM This is the same as https://reviews.llvm.org/D46505 and https://reviews.llvm.org/D59208, but for signed opcodes. --- llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 27 ++++++++++++++++- llvm/test/CodeGen/AArch64/i128-math.ll | 25 ++++++++++++++++ llvm/test/CodeGen/X86/addcarry.ll | 29 +++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index be654b1d6591b..484a6231b7f65 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -430,6 +430,8 @@ namespace { SDValue visitSADDO_CARRY(SDNode *N); SDValue visitUADDO_CARRYLike(SDValue N0, SDValue N1, SDValue CarryIn, SDNode *N); + SDValue visitSADDO_CARRYLike(SDValue N0, SDValue N1, SDValue CarryIn, + SDNode *N); SDValue visitSUBE(SDNode *N); SDValue visitUSUBO_CARRY(SDNode *N); SDValue visitSSUBO_CARRY(SDNode *N); @@ -3305,7 +3307,12 @@ SDValue DAGCombiner::visitADDO(SDNode *N) { return CombineTo(N, DAG.getNode(ISD::ADD, DL, VT, N0, N1), DAG.getConstant(0, DL, CarryVT)); - if (!IsSigned) { + if (IsSigned) { + // fold (saddo (xor a, -1), 1) -> (ssub 0, a). + if (isBitwiseNot(N0) && isOneOrOneSplat(N1)) + return DAG.getNode(ISD::SSUBO, DL, N->getVTList(), + DAG.getConstant(0, DL, VT), N0.getOperand(0)); + } else { // fold (uaddo (xor a, -1), 1) -> (usub 0, a) and flip carry. if (isBitwiseNot(N0) && isOneOrOneSplat(N1)) { SDValue Sub = DAG.getNode(ISD::USUBO, DL, N->getVTList(), @@ -3637,6 +3644,18 @@ SDValue DAGCombiner::visitUADDO_CARRYLike(SDValue N0, SDValue N1, return SDValue(); } +SDValue DAGCombiner::visitSADDO_CARRYLike(SDValue N0, SDValue N1, + SDValue CarryIn, SDNode *N) { + // fold (saddo_carry (xor a, -1), b, c) -> (ssubo_carry b, a, !c) + if (isBitwiseNot(N0)) { + if (SDValue NotC = extractBooleanFlip(CarryIn, DAG, TLI, true)) + return DAG.getNode(ISD::SSUBO_CARRY, SDLoc(N), N->getVTList(), N1, + N0.getOperand(0), NotC); + } + + return SDValue(); +} + SDValue DAGCombiner::visitSADDO_CARRY(SDNode *N) { SDValue N0 = N->getOperand(0); SDValue N1 = N->getOperand(1); @@ -3656,6 +3675,12 @@ SDValue DAGCombiner::visitSADDO_CARRY(SDNode *N) { return DAG.getNode(ISD::SADDO, DL, N->getVTList(), N0, N1); } + if (SDValue Combined = visitSADDO_CARRYLike(N0, N1, CarryIn, N)) + return Combined; + + if (SDValue Combined = visitSADDO_CARRYLike(N1, N0, CarryIn, N)) + return Combined; + return SDValue(); } diff --git a/llvm/test/CodeGen/AArch64/i128-math.ll b/llvm/test/CodeGen/AArch64/i128-math.ll index 2f7848de82274..7c1d9141421fd 100644 --- a/llvm/test/CodeGen/AArch64/i128-math.ll +++ b/llvm/test/CodeGen/AArch64/i128-math.ll @@ -430,3 +430,28 @@ define i128 @i128_saturating_mul(i128 %x, i128 %y) { %7 = select i1 %3, i128 %6, i128 %2 ret i128 %7 } + +define { i128, i1 } @saddo_not_1(i128 %x) nounwind { +; CHECK-LABEL: saddo_not_1: +; CHECK: // %bb.0: +; CHECK-NEXT: negs x0, x0 +; CHECK-NEXT: ngcs x1, x1 +; CHECK-NEXT: cset w2, vs +; CHECK-NEXT: ret + %not = xor i128 %x, -1 + %r = call { i128, i1 } @llvm.sadd.with.overflow.i128(i128 %not, i128 1) + ret { i128, i1 } %r +} + +define { i128, i1 } @saddo_carry_not_1(i128 %x) nounwind { +; CHECK-LABEL: saddo_carry_not_1: +; CHECK: // %bb.0: +; CHECK-NEXT: mov w8, #1 // =0x1 +; CHECK-NEXT: negs x0, x0 +; CHECK-NEXT: sbcs x1, x8, x1 +; CHECK-NEXT: cset w2, vs +; CHECK-NEXT: ret + %not = xor i128 %x, -1 + %r = call { i128, i1 } @llvm.sadd.with.overflow.i128(i128 %not, i128 u0x10000000000000001) + ret { i128, i1 } %r +} diff --git a/llvm/test/CodeGen/X86/addcarry.ll b/llvm/test/CodeGen/X86/addcarry.ll index 231645f641591..3fc4ed99fad0f 100644 --- a/llvm/test/CodeGen/X86/addcarry.ll +++ b/llvm/test/CodeGen/X86/addcarry.ll @@ -4,6 +4,7 @@ declare { i8, i64 } @llvm.x86.addcarry.64(i8, i64, i64) declare { i64, i1 } @llvm.uadd.with.overflow.i64(i64, i64) #1 declare { i64, i1 } @llvm.usub.with.overflow.i64(i64, i64) #1 +declare { i128, i1 } @llvm.sadd.with.overflow.i128(i128, i128) define i128 @add128(i128 %a, i128 %b) nounwind { ; CHECK-LABEL: add128: @@ -388,6 +389,34 @@ define i128 @addcarry1_not(i128 %n) nounwind { ret i128 %2 } +define { i128, i1 } @saddo_not_1(i128 %x) nounwind { +; CHECK-LABEL: saddo_not_1: +; CHECK: # %bb.0: +; CHECK-NEXT: movq %rdi, %rax +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: negq %rax +; CHECK-NEXT: sbbq %rsi, %rdx +; CHECK-NEXT: seto %cl +; CHECK-NEXT: retq + %not = xor i128 %x, -1 + %r = call { i128, i1 } @llvm.sadd.with.overflow.i128(i128 %not, i128 1) + ret { i128, i1 } %r +} + +define { i128, i1 } @saddo_carry_not_1(i128 %x) nounwind { +; CHECK-LABEL: saddo_carry_not_1: +; CHECK: # %bb.0: +; CHECK-NEXT: movq %rdi, %rax +; CHECK-NEXT: negq %rax +; CHECK-NEXT: movl $1, %edx +; CHECK-NEXT: sbbq %rsi, %rdx +; CHECK-NEXT: seto %cl +; CHECK-NEXT: retq + %not = xor i128 %x, -1 + %r = call { i128, i1 } @llvm.sadd.with.overflow.i128(i128 %not, i128 u0x10000000000000001) + ret { i128, i1 } %r +} + define i128 @addcarry_to_subcarry(i64 %a, i64 %b) nounwind { ; CHECK-LABEL: addcarry_to_subcarry: ; CHECK: # %bb.0: