[llvm] [X86][CodeGen] Support lowering for CCMP/CTEST (PR #91747)

Shengchen Kan via llvm-commits llvm-commits at lists.llvm.org
Tue May 21 00:42:35 PDT 2024


================
@@ -54605,7 +54610,132 @@ static bool onlyZeroFlagUsed(SDValue Flags) {
   return true;
 }
 
+static SDValue combineX86SubCmpToCcmpCtest(SDNode *N, SDValue Flag,
+                                           SelectionDAG &DAG,
+                                           TargetLowering::DAGCombinerInfo &DCI,
+                                           const X86Subtarget &ST) {
+  // cmp(and/or(setcc(cc0, flag0), setcc(cc1, sub (X, Y))), 0)
+  // brcond ne
+  //
+  //  ->
+  //
+  // ccmp(X, Y, cflags/~cflags, cc0/~cc0, flag0)
+  // brcond cc1
+  //
+  //
+  // sub(and/or(setcc(cc0, flag0), setcc(cc1, sub (X, Y))), 1)
+  // brcond ne
+  //
+  // ->
+  //
+  // ccmp(X, Y, cflags/~cflags, cc0/~cc0, flag0)
+  // brcond ~cc1
+
+  // cmp(and/or(setcc(cc0, flag0), setcc(cc1, cmp (X, 0))), 0)
+  // brcond ne
+  //
+  //  ->
+  //
+  // ctest(X, X, cflags/~cflags, cc0/~cc0, flag0)
+  // brcond cc1
+  //
+  //
+  // sub(and/or(setcc(cc0, flag0), setcc(cc1, cmp (X, 0))), 1)
+  // brcond ne
+  //
+  //  ->
+  //
+  // ctest(X, X, cflags/~cflags, cc0/~cc0, flag0)
+  // brcond ~cc1
+
+  // if only flag has users, where cflags is determined by cc1.
+
+  SDValue LHS = N->getOperand(0);
+
+  if (!ST.hasCCMP() ||
+      (LHS.getOpcode() != ISD::AND && LHS.getOpcode() != ISD::OR) ||
+      !Flag.hasOneUse())
+    return SDValue();
+
+  SDValue SetCC0 = LHS.getOperand(0);
+  SDValue SetCC1 = LHS.getOperand(1);
+  if (SetCC0.getOpcode() != X86ISD::SETCC ||
+      SetCC1.getOpcode() != X86ISD::SETCC)
+    return SDValue();
+
+  auto GetCombineToOpc = [&](SDValue V) {
+    SDValue Op = V.getOperand(1);
+    unsigned Opc = Op.getOpcode();
+    return (Opc == X86ISD::SUB) ? X86ISD::CCMP
+           : (Opc == X86ISD::CMP && isNullConstant(Op.getOperand(1)))
+               ? X86ISD::CTEST
+               : 0U;
+  };
+
+  unsigned NewOpc = 0;
+
+  // and/or is commutable. Try to commute the operands and then test again.
+  if (!(NewOpc = GetCombineToOpc(SetCC1))) {
+    std::swap(SetCC0, SetCC1);
+    if (!(NewOpc = GetCombineToOpc(SetCC1)))
+      return SDValue();
+  }
+
+  SDValue Sub = SetCC1.getOperand(1);
+  SDNode *BrCond = *Flag->uses().begin();
+  if (BrCond->getOpcode() != X86ISD::BRCOND)
+    return SDValue();
----------------
KanRobert wrote:

We check `Flag.hasOneUse()` at the begin of the function.

> BRCOND + non-BRCOND

I think it never occurs in practice. We can always move non-BRCOND to successor in this case, e.g.

```
int f(int a, int b, int *c) {
  if (a < b) {
    *c = a;
    return a;
  }
  return b;
}
```

```
f:                                      # @f
        .cfi_startproc
# %bb.0:                                # %entry
        movl    %esi, %eax
        cmpl    %esi, %edi
        jge     .LBB0_2
# %bb.1:                                # %if.then
        movl    %edi, (%rdx)
        movl    %edi, %eax
.LBB0_2:                                # %return
        retq
```

> multiple BRCOND

Not encountered in my experience. We can enhance the combine if we really find the case.

https://github.com/llvm/llvm-project/pull/91747


More information about the llvm-commits mailing list