Skip to content

Conversation

@diggerlin
Copy link
Contributor

Based on the discussion in #158657 (comment),
We can convert (setcc (and X, 1), 0, eq) to XORI (and X, 1), 1 and save one instruction.

@llvmbot
Copy link
Member

llvmbot commented Nov 17, 2025

@llvm/pr-subscribers-backend-powerpc

Author: zhijian lin (diggerlin)

Changes

Based on the discussion in #158657 (comment),
We can convert (setcc (and X, 1), 0, eq) to XORI (and X, 1), 1 and save one instruction.


Full diff: https://github.com/llvm/llvm-project/pull/168384.diff

2 Files Affected:

  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+71)
  • (modified) llvm/test/CodeGen/PowerPC/memCmpUsedInZeroEqualityComparison.ll (+1-2)
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp index f55336bafd251..ea0e31ae47082 100644 --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -15775,11 +15775,82 @@ SDValue convertTwoLoadsAndCmpToVCMPEQUB(SelectionDAG &DAG, SDNode *N, CC == ISD::SETNE ? ISD::SETEQ : ISD::SETNE); } +// Detect whether there is a pattern like (setcc (and X, 1), 0, eq). +// If it is , return true; otherwise return false. +static bool canConvertSETCCToXori(SDNode *N) { + if (N->getOpcode() != ISD::SETCC) + return false; + + ISD::CondCode CC = cast<CondCodeSDNode>(N->getOperand(2))->get(); + if (CC != ISD::SETEQ) + return false; + + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + + // Check the `SDValue &V` is from `and` with `1`. + auto IsAndWithOne = [](SDValue &V) { + if (V.getOpcode() == ISD::AND) { + SDNode *AndNode = V.getNode(); + for (const SDValue &Op : AndNode->ops()) + if (auto *C = dyn_cast<ConstantSDNode>(Op)) + if (C->isOne()) + return true; + } + return false; + }; + + // Check whether the SETCC compare with zero. + auto IsCompareWithZero = [](SDValue &V) { + if (auto *C = dyn_cast<ConstantSDNode>(V)) + if (C->isZero()) + return true; + return false; + }; + + return (IsAndWithOne(LHS) && IsCompareWithZero(RHS)) || + (IsAndWithOne(RHS) && IsCompareWithZero(LHS)); +} + +// You must check whether the `SDNode* N` can be converted to Xori using +// the function `static bool canConvertSETCCToXori(SDNode *N)` +// before calling the function; otherwise, it may produce incorrect results. +static SDValue ConvertSETCCToXori(SDNode *N, SelectionDAG &DAG) { + + assert(N->getOpcode() == ISD::SETCC && "Should SETCC SDNode here."); + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + SDLoc DL(N); + + ISD::CondCode CC = cast<CondCodeSDNode>(N->getOperand(2))->get(); + assert((CC == ISD::SETEQ) && "CC must be ISD::SETEQ."); + // Rewrite it as XORI (and X, 1), 1. + auto MakeXor1 = [&](SDValue V) { + EVT VT = V.getValueType(); + SDValue One = DAG.getConstant(1, DL, VT); + SDValue Xor = DAG.getNode(ISD::XOR, DL, VT, V, One); + return DAG.getNode(ISD::TRUNCATE, DL, MVT::i1, Xor); + }; + + if (LHS.getOpcode() == ISD::AND && RHS.getOpcode() != ISD::AND) + return MakeXor1(LHS); + + if (RHS.getOpcode() == ISD::AND && LHS.getOpcode() != ISD::AND) + return MakeXor1(RHS); + + llvm_unreachable("Should not reach here."); +} + SDValue PPCTargetLowering::combineSetCC(SDNode *N, DAGCombinerInfo &DCI) const { assert(N->getOpcode() == ISD::SETCC && "Should be called with a SETCC node"); + // Check if the pattern (setcc (and X, 1), 0, eq) is present. + // If it is, rewrite it as XORI (and X, 1), 1. + if (canConvertSETCCToXori(N)) + return ConvertSETCCToXori(N, DCI.DAG); + ISD::CondCode CC = cast<CondCodeSDNode>(N->getOperand(2))->get(); if (CC == ISD::SETNE || CC == ISD::SETEQ) { SDValue LHS = N->getOperand(0); diff --git a/llvm/test/CodeGen/PowerPC/memCmpUsedInZeroEqualityComparison.ll b/llvm/test/CodeGen/PowerPC/memCmpUsedInZeroEqualityComparison.ll index bf86695818689..8d4dce122a437 100644 --- a/llvm/test/CodeGen/PowerPC/memCmpUsedInZeroEqualityComparison.ll +++ b/llvm/test/CodeGen/PowerPC/memCmpUsedInZeroEqualityComparison.ll @@ -39,9 +39,8 @@ define signext i32 @zeroEqualityTest01(ptr %x, ptr %y) { ; CHECK-NEXT: lxvd2x 35, 0, 3 ; CHECK-NEXT: vcmpequb. 2, 3, 2 ; CHECK-NEXT: mfocrf 3, 2 +; CHECK-NEXT: not 3, 3 ; CHECK-NEXT: rlwinm 3, 3, 25, 31, 31 -; CHECK-NEXT: cntlzw 3, 3 -; CHECK-NEXT: srwi 3, 3, 5 ; CHECK-NEXT: blr %call = tail call signext i32 @memcmp(ptr %x, ptr %y, i64 16) %not.tobool = icmp ne i32 %call, 0 
@AZero13
Copy link
Contributor

AZero13 commented Nov 17, 2025

should this really be specific to powerpc

@RolandF77
Copy link
Collaborator

should this really be specific to powerpc

It changes 2 ISD operations into 2 ISD operations. At the platform independent level it is not clearly an optimization.

@diggerlin diggerlin requested a review from amy-kwan November 17, 2025 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants