[llvm] r284248 - [X86] Take advantage of the lzcnt instruction on btver2 architectures when ORing comparisons to zero.

Pierre Gousseau via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 14 09:41:38 PDT 2016


Author: pgousseau
Date: Fri Oct 14 11:41:38 2016
New Revision: 284248

URL: http://llvm.org/viewvc/llvm-project?rev=284248&view=rev
Log:
[X86] Take advantage of the lzcnt instruction on btver2 architectures when ORing comparisons to zero.

This change adds transformations such as:
  zext(or(setcc(eq, (cmp x, 0)), setcc(eq, (cmp y, 0))))
  To:
  srl(or(ctlz(x), ctlz(y)), log2(bitsize(x))
This optimisation is beneficial on Jaguar architecture only, where lzcnt has a good reciprocal throughput.
Other architectures such as Intel's Haswell/Broadwell or AMD's Bulldozer/PileDriver do not benefit from it.
For this reason the change also adds a "HasFastLZCNT" feature which gets enabled for Jaguar.

Differential Revision: https://reviews.llvm.org/D23446

Added:
    llvm/trunk/test/CodeGen/X86/lzcnt-zext-cmp.ll
Modified:
    llvm/trunk/lib/Target/X86/X86.td
    llvm/trunk/lib/Target/X86/X86ISelLowering.cpp
    llvm/trunk/lib/Target/X86/X86ISelLowering.h
    llvm/trunk/lib/Target/X86/X86InstrInfo.td
    llvm/trunk/lib/Target/X86/X86Subtarget.cpp
    llvm/trunk/lib/Target/X86/X86Subtarget.h

Modified: llvm/trunk/lib/Target/X86/X86.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86.td?rev=284248&r1=284247&r2=284248&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86.td (original)
+++ llvm/trunk/lib/Target/X86/X86.td Fri Oct 14 11:41:38 2016
@@ -262,6 +262,12 @@ def FeatureFastScalarFSQRT
 def FeatureFastVectorFSQRT
     : SubtargetFeature<"fast-vector-fsqrt", "HasFastVectorFSQRT",
                        "true", "Vector SQRT is fast (disable Newton-Raphson)">;
+// If lzcnt has equivalent latency/throughput to most simple integer ops, it can
+// be used to replace test/set sequences.
+def FeatureFastLZCNT
+    : SubtargetFeature<
+          "fast-lzcnt", "HasFastLZCNT", "true",
+          "LZCNT instructions are as fast as most simple integer ops">;
 
 //===----------------------------------------------------------------------===//
 // X86 processors supported.
@@ -646,6 +652,7 @@ def : ProcessorModel<"btver2", BtVer2Mod
   FeatureF16C,
   FeatureMOVBE,
   FeatureLZCNT,
+  FeatureFastLZCNT,
   FeaturePOPCNT,
   FeatureXSAVE,
   FeatureXSAVEOPT,

Modified: llvm/trunk/lib/Target/X86/X86ISelLowering.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86ISelLowering.cpp?rev=284248&r1=284247&r2=284248&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86ISelLowering.cpp (original)
+++ llvm/trunk/lib/Target/X86/X86ISelLowering.cpp Fri Oct 14 11:41:38 2016
@@ -4178,6 +4178,10 @@ bool X86TargetLowering::isCheapToSpecula
   return Subtarget.hasLZCNT();
 }
 
+bool X86TargetLowering::isCtlzFast() const {
+  return Subtarget.hasFastLZCNT();
+}
+
 bool X86TargetLowering::hasAndNotCompare(SDValue Y) const {
   if (!Subtarget.hasBMI())
     return false;
@@ -29090,6 +29094,113 @@ static SDValue combineLogicBlendIntoPBLE
   return DAG.getBitcast(VT, Mask);
 }
 
+// Helper function for combineOrCmpEqZeroToCtlzSrl
+// Transforms:
+//   seteq(cmp x, 0)
+//   into:
+//   srl(ctlz x), log2(bitsize(x))
+// Input pattern is checked by caller.
+SDValue lowerX86CmpEqZeroToCtlzSrl(SDValue Op, EVT ExtTy, SelectionDAG &DAG) {
+  SDValue Cmp = Op.getOperand(1);
+  EVT VT = Cmp.getOperand(0).getValueType();
+  unsigned Log2b = Log2_32(VT.getSizeInBits());
+  SDLoc dl(Op);
+  SDValue Clz = DAG.getNode(ISD::CTLZ, dl, VT, Cmp->getOperand(0));
+  // The result of the shift is true or false, and on X86, the 32-bit
+  // encoding of shr and lzcnt is more desirable.
+  SDValue Trunc = DAG.getZExtOrTrunc(Clz, dl, MVT::i32);
+  SDValue Scc = DAG.getNode(ISD::SRL, dl, MVT::i32, Trunc,
+                            DAG.getConstant(Log2b, dl, VT));
+  return DAG.getZExtOrTrunc(Scc, dl, ExtTy);
+}
+
+// Try to transform:
+//   zext(or(setcc(eq, (cmp x, 0)), setcc(eq, (cmp y, 0))))
+//   into:
+//   srl(or(ctlz(x), ctlz(y)), log2(bitsize(x))
+// Will also attempt to match more generic cases, eg:
+//   zext(or(or(setcc(eq, cmp 0), setcc(eq, cmp 0)), setcc(eq, cmp 0)))
+// Only applies if the target supports the FastLZCNT feature.
+static SDValue combineOrCmpEqZeroToCtlzSrl(SDNode *N, SelectionDAG &DAG,
+                                           TargetLowering::DAGCombinerInfo &DCI,
+                                           const X86Subtarget &Subtarget) {
+  if (DCI.isBeforeLegalize() || !Subtarget.getTargetLowering()->isCtlzFast())
+    return SDValue();
+
+  auto isORCandidate = [](SDValue N) {
+    return (N->getOpcode() == ISD::OR && N->hasOneUse());
+  };
+
+  // Check the zero extend is extending to 32-bit or more. The code generated by
+  // srl(ctlz) for 16-bit or less variants of the pattern would require extra
+  // instructions to clear the upper bits.
+  if (!N->hasOneUse() || !N->getSimpleValueType(0).bitsGE(MVT::i32) ||
+      !isORCandidate(N->getOperand(0)))
+    return SDValue();
+
+  // Check the node matches: setcc(eq, cmp 0)
+  auto isSetCCCandidate = [](SDValue N) {
+    return N->getOpcode() == X86ISD::SETCC && N->hasOneUse() &&
+           X86::CondCode(N->getConstantOperandVal(0)) == X86::COND_E &&
+           N->getOperand(1).getOpcode() == X86ISD::CMP &&
+           N->getOperand(1).getConstantOperandVal(1) == 0 &&
+           N->getOperand(1).getValueType().bitsGE(MVT::i32);
+  };
+
+  SDNode *OR = N->getOperand(0).getNode();
+  SDValue LHS = OR->getOperand(0);
+  SDValue RHS = OR->getOperand(1);
+
+  // Save nodes matching or(or, setcc(eq, cmp 0)).
+  SmallVector<SDNode *, 2> ORNodes;
+  while (((isORCandidate(LHS) && isSetCCCandidate(RHS)) ||
+          (isORCandidate(RHS) && isSetCCCandidate(LHS)))) {
+    ORNodes.push_back(OR);
+    OR = (LHS->getOpcode() == ISD::OR) ? LHS.getNode() : RHS.getNode();
+    LHS = OR->getOperand(0);
+    RHS = OR->getOperand(1);
+  }
+
+  // The last OR node should match or(setcc(eq, cmp 0), setcc(eq, cmp 0)).
+  if (!(isSetCCCandidate(LHS) && isSetCCCandidate(RHS)) ||
+      !isORCandidate(SDValue(OR, 0)))
+    return SDValue();
+
+  // We have a or(setcc(eq, cmp 0), setcc(eq, cmp 0)) pattern, try to lower it
+  // to
+  // or(srl(ctlz),srl(ctlz)).
+  // The dag combiner can then fold it into:
+  // srl(or(ctlz, ctlz)).
+  EVT VT = OR->getValueType(0);
+  SDValue NewLHS = lowerX86CmpEqZeroToCtlzSrl(LHS, VT, DAG);
+  SDValue Ret, NewRHS;
+  if (NewLHS && (NewRHS = lowerX86CmpEqZeroToCtlzSrl(RHS, VT, DAG)))
+    Ret = DAG.getNode(ISD::OR, SDLoc(OR), VT, NewLHS, NewRHS);
+
+  if (!Ret)
+    return SDValue();
+
+  // Try to lower nodes matching the or(or, setcc(eq, cmp 0)) pattern.
+  while (ORNodes.size() > 0) {
+    OR = ORNodes.pop_back_val();
+    LHS = OR->getOperand(0);
+    RHS = OR->getOperand(1);
+    // Swap rhs with lhs to match or(setcc(eq, cmp, 0), or).
+    if (RHS->getOpcode() == ISD::OR)
+      std::swap(LHS, RHS);
+    EVT VT = OR->getValueType(0);
+    SDValue NewRHS = lowerX86CmpEqZeroToCtlzSrl(RHS, VT, DAG);
+    if (!NewRHS)
+      return SDValue();
+    Ret = DAG.getNode(ISD::OR, SDLoc(OR), VT, Ret, NewRHS);
+  }
+
+  if (Ret)
+    Ret = DAG.getNode(ISD::ZERO_EXTEND, SDLoc(N), N->getValueType(0), Ret);
+
+  return Ret;
+}
+
 static SDValue combineOr(SDNode *N, SelectionDAG &DAG,
                          TargetLowering::DAGCombinerInfo &DCI,
                          const X86Subtarget &Subtarget) {
@@ -31121,6 +31232,9 @@ static SDValue combineZext(SDNode *N, Se
   if (SDValue NewAdd = promoteExtBeforeAdd(N, DAG, Subtarget))
     return NewAdd;
 
+  if (SDValue R = combineOrCmpEqZeroToCtlzSrl(N, DAG, DCI, Subtarget))
+    return R;
+
   return SDValue();
 }
 

Modified: llvm/trunk/lib/Target/X86/X86ISelLowering.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86ISelLowering.h?rev=284248&r1=284247&r2=284248&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86ISelLowering.h (original)
+++ llvm/trunk/lib/Target/X86/X86ISelLowering.h Fri Oct 14 11:41:38 2016
@@ -771,6 +771,8 @@ namespace llvm {
 
     bool isCheapToSpeculateCtlz() const override;
 
+    bool isCtlzFast() const override;
+
     bool hasBitPreservingFPLogic(EVT VT) const override {
       return VT == MVT::f32 || VT == MVT::f64 || VT.isVector();
     }

Modified: llvm/trunk/lib/Target/X86/X86InstrInfo.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86InstrInfo.td?rev=284248&r1=284247&r2=284248&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86InstrInfo.td (original)
+++ llvm/trunk/lib/Target/X86/X86InstrInfo.td Fri Oct 14 11:41:38 2016
@@ -890,6 +890,7 @@ def CallImmAddr  : Predicate<"Subtarget-
 def FavorMemIndirectCall  : Predicate<"!Subtarget->callRegIndirect()">;
 def NotSlowIncDec : Predicate<"!Subtarget->slowIncDec()">;
 def HasFastMem32 : Predicate<"!Subtarget->isUnalignedMem32Slow()">;
+def HasFastLZCNT : Predicate<"Subtarget->hasFastLZCNT()">;
 def HasMFence    : Predicate<"Subtarget->hasMFence()">;
 
 //===----------------------------------------------------------------------===//

Modified: llvm/trunk/lib/Target/X86/X86Subtarget.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86Subtarget.cpp?rev=284248&r1=284247&r2=284248&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86Subtarget.cpp (original)
+++ llvm/trunk/lib/Target/X86/X86Subtarget.cpp Fri Oct 14 11:41:38 2016
@@ -284,6 +284,7 @@ void X86Subtarget::initializeEnvironment
   HasFastPartialYMMWrite = false;
   HasFastScalarFSQRT = false;
   HasFastVectorFSQRT = false;
+  HasFastLZCNT = false;
   HasSlowDivide32 = false;
   HasSlowDivide64 = false;
   PadShortFunctions = false;

Modified: llvm/trunk/lib/Target/X86/X86Subtarget.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/X86/X86Subtarget.h?rev=284248&r1=284247&r2=284248&view=diff
==============================================================================
--- llvm/trunk/lib/Target/X86/X86Subtarget.h (original)
+++ llvm/trunk/lib/Target/X86/X86Subtarget.h Fri Oct 14 11:41:38 2016
@@ -215,6 +215,9 @@ protected:
   /// 64-bit divisions and should be used when possible.
   bool HasSlowDivide64;
 
+  /// True if LZCNT instruction is fast.
+  bool HasFastLZCNT;
+
   /// True if the short functions should be padded to prevent
   /// a stall when returning too early.
   bool PadShortFunctions;
@@ -444,6 +447,7 @@ public:
   bool hasFastPartialYMMWrite() const { return HasFastPartialYMMWrite; }
   bool hasFastScalarFSQRT() const { return HasFastScalarFSQRT; }
   bool hasFastVectorFSQRT() const { return HasFastVectorFSQRT; }
+  bool hasFastLZCNT() const { return HasFastLZCNT; }
   bool hasSlowDivide32() const { return HasSlowDivide32; }
   bool hasSlowDivide64() const { return HasSlowDivide64; }
   bool padShortFunctions() const { return PadShortFunctions; }

Added: llvm/trunk/test/CodeGen/X86/lzcnt-zext-cmp.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/X86/lzcnt-zext-cmp.ll?rev=284248&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/X86/lzcnt-zext-cmp.ll (added)
+++ llvm/trunk/test/CodeGen/X86/lzcnt-zext-cmp.ll Fri Oct 14 11:41:38 2016
@@ -0,0 +1,341 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; Test patterns which generates lzcnt instructions.
+; Eg: zext(or(setcc(cmp), setcc(cmp))) -> shr(or(lzcnt, lzcnt))
+; RUN: llc < %s -mtriple=x86_64-pc-linux -mcpu=btver2 | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-pc-linux -mcpu=btver2 -mattr=-fast-lzcnt | FileCheck --check-prefix=NOFASTLZCNT %s
+
+; Test one 32-bit input, output is 32-bit, no transformations expected.
+define i32 @test_zext_cmp0(i32 %a) {
+; CHECK-LABEL: test_zext_cmp0:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    testl %edi, %edi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp0:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    xorl %eax, %eax
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %conv = zext i1 %cmp to i32
+  ret i32 %conv
+}
+
+; Test two 32-bit inputs, output is 32-bit.
+define i32 @test_zext_cmp1(i32 %a, i32 %b) {
+; CHECK-LABEL: test_zext_cmp1:
+; CHECK:       # BB#0:
+; CHECK-NEXT:    lzcntl %edi, %ecx
+; CHECK-NEXT:    lzcntl %esi, %eax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    shrl $5, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp1:
+; NOFASTLZCNT:       # BB#0:
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testl %esi, %esi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+  %cmp = icmp eq i32 %a, 0
+  %cmp1 = icmp eq i32 %b, 0
+  %or = or i1 %cmp, %cmp1
+  %lor.ext = zext i1 %or to i32
+  ret i32 %lor.ext
+}
+
+; Test two 64-bit inputs, output is 64-bit.
+define i64 @test_zext_cmp2(i64 %a, i64 %b) {
+; CHECK-LABEL: test_zext_cmp2:
+; CHECK:       # BB#0:
+; CHECK-NEXT:    lzcntq %rdi, %rcx
+; CHECK-NEXT:    lzcntq %rsi, %rax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    shrl $6, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp2:
+; NOFASTLZCNT:       # BB#0:
+; NOFASTLZCNT-NEXT:    testq %rdi, %rdi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testq %rsi, %rsi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+  %cmp = icmp eq i64 %a, 0
+  %cmp1 = icmp eq i64 %b, 0
+  %or = or i1 %cmp, %cmp1
+  %lor.ext = zext i1 %or to i64
+  ret i64 %lor.ext
+}
+
+; Test two 16-bit inputs, output is 16-bit.
+; The transform is disabled for the 16-bit case, as we still have to clear the
+; upper 16-bits, adding one more instruction.
+define i16 @test_zext_cmp3(i16 %a, i16 %b) {
+; CHECK-LABEL: test_zext_cmp3:
+; CHECK:       # BB#0:
+; CHECK-NEXT:    testw %di, %di
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    testw %si, %si
+; CHECK-NEXT:    sete %cl
+; CHECK-NEXT:    orb %al, %cl
+; CHECK-NEXT:    movzbl %cl, %eax
+; CHECK-NEXT:    # kill: %AX<def> %AX<kill> %EAX<kill>
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp3:
+; NOFASTLZCNT:       # BB#0:
+; NOFASTLZCNT-NEXT:    testw %di, %di
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testw %si, %si
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    # kill: %AX<def> %AX<kill> %EAX<kill>
+; NOFASTLZCNT-NEXT:    retq
+  %cmp = icmp eq i16 %a, 0
+  %cmp1 = icmp eq i16 %b, 0
+  %or = or i1 %cmp, %cmp1
+  %lor.ext = zext i1 %or to i16
+  ret i16 %lor.ext
+}
+
+; Test two 32-bit inputs, output is 64-bit.
+define i64 @test_zext_cmp4(i32 %a, i32 %b) {
+; CHECK-LABEL: test_zext_cmp4:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    lzcntl %edi, %ecx
+; CHECK-NEXT:    lzcntl %esi, %eax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    shrl $5, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp4:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testl %esi, %esi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %cmp1 = icmp eq i32 %b, 0
+  %0 = or i1 %cmp, %cmp1
+  %conv = zext i1 %0 to i64
+  ret i64 %conv
+}
+
+; Test two 64-bit inputs, output is 32-bit.
+define i32 @test_zext_cmp5(i64 %a, i64 %b) {
+; CHECK-LABEL: test_zext_cmp5:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    lzcntq %rdi, %rcx
+; CHECK-NEXT:    lzcntq %rsi, %rax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    shrl $6, %eax
+; CHECK-NEXT:    # kill: %EAX<def> %EAX<kill> %RAX<kill>
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp5:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    testq %rdi, %rdi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testq %rsi, %rsi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i64 %a, 0
+  %cmp1 = icmp eq i64 %b, 0
+  %0 = or i1 %cmp, %cmp1
+  %lor.ext = zext i1 %0 to i32
+  ret i32 %lor.ext
+}
+
+; Test three 32-bit inputs, output is 32-bit.
+define i32 @test_zext_cmp6(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: test_zext_cmp6:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    lzcntl %edi, %eax
+; CHECK-NEXT:    lzcntl %esi, %ecx
+; CHECK-NEXT:    orl %eax, %ecx
+; CHECK-NEXT:    lzcntl %edx, %eax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    shrl $5, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp6:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testl %esi, %esi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    testl %edx, %edx
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    orb %cl, %al
+; NOFASTLZCNT-NEXT:    movzbl %al, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %cmp1 = icmp eq i32 %b, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %c, 0
+  %.cmp2 = or i1 %or.cond, %cmp2
+  %lor.ext = zext i1 %.cmp2 to i32
+  ret i32 %lor.ext
+}
+
+; Test three 32-bit inputs, output is 32-bit, but compared to test_zext_cmp6 test,
+; %.cmp2 inputs' order is inverted.
+define i32 @test_zext_cmp7(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: test_zext_cmp7:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    lzcntl %edi, %eax
+; CHECK-NEXT:    lzcntl %esi, %ecx
+; CHECK-NEXT:    orl %eax, %ecx
+; CHECK-NEXT:    lzcntl %edx, %eax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    shrl $5, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp7:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testl %esi, %esi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    testl %edx, %edx
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    orb %cl, %al
+; NOFASTLZCNT-NEXT:    movzbl %al, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %cmp1 = icmp eq i32 %b, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp2 = icmp eq i32 %c, 0
+  %.cmp2 = or i1 %cmp2, %or.cond
+  %lor.ext = zext i1 %.cmp2 to i32
+  ret i32 %lor.ext
+}
+
+; Test four 32-bit inputs, output is 32-bit.
+define i32 @test_zext_cmp8(i32 %a, i32 %b, i32 %c, i32 %d) {
+; CHECK-LABEL: test_zext_cmp8:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    lzcntl %edi, %eax
+; CHECK-NEXT:    lzcntl %esi, %esi
+; CHECK-NEXT:    lzcntl %edx, %edx
+; CHECK-NEXT:    orl %eax, %esi
+; CHECK-NEXT:    lzcntl %ecx, %eax
+; CHECK-NEXT:    orl %edx, %eax
+; CHECK-NEXT:    orl %esi, %eax
+; CHECK-NEXT:    shrl $5, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp8:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %dil
+; NOFASTLZCNT-NEXT:    testl %esi, %esi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    orb %dil, %al
+; NOFASTLZCNT-NEXT:    testl %edx, %edx
+; NOFASTLZCNT-NEXT:    sete %dl
+; NOFASTLZCNT-NEXT:    testl %ecx, %ecx
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %dl, %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %cmp1 = icmp eq i32 %b, 0
+  %or.cond = or i1 %cmp, %cmp1
+  %cmp3 = icmp eq i32 %c, 0
+  %or.cond5 = or i1 %or.cond, %cmp3
+  %cmp4 = icmp eq i32 %d, 0
+  %.cmp4 = or i1 %or.cond5, %cmp4
+  %lor.ext = zext i1 %.cmp4 to i32
+  ret i32 %lor.ext
+}
+
+; Test one 32-bit input, one 64-bit input, output is 32-bit.
+define i32 @test_zext_cmp9(i32 %a, i64 %b) {
+; CHECK-LABEL: test_zext_cmp9:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    lzcntq %rsi, %rax
+; CHECK-NEXT:    lzcntl %edi, %ecx
+; CHECK-NEXT:    shrl $5, %ecx
+; CHECK-NEXT:    shrl $6, %eax
+; CHECK-NEXT:    orl %ecx, %eax
+; CHECK-NEXT:    # kill: %EAX<def> %EAX<kill> %RAX<kill>
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp9:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    testl %edi, %edi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    testq %rsi, %rsi
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %cmp1 = icmp eq i64 %b, 0
+  %0 = or i1 %cmp, %cmp1
+  %lor.ext = zext i1 %0 to i32
+  ret i32 %lor.ext
+}
+
+; Test 2 128-bit inputs, output is 32-bit, no transformations expected.
+define i32 @test_zext_cmp10(i64 %a.coerce0, i64 %a.coerce1, i64 %b.coerce0, i64 %b.coerce1) {
+; CHECK-LABEL: test_zext_cmp10:
+; CHECK:       # BB#0: # %entry
+; CHECK-NEXT:    orq %rsi, %rdi
+; CHECK-NEXT:    sete %al
+; CHECK-NEXT:    orq %rcx, %rdx
+; CHECK-NEXT:    sete %cl
+; CHECK-NEXT:    orb %al, %cl
+; CHECK-NEXT:    movzbl %cl, %eax
+; CHECK-NEXT:    retq
+;
+; NOFASTLZCNT-LABEL: test_zext_cmp10:
+; NOFASTLZCNT:       # BB#0: # %entry
+; NOFASTLZCNT-NEXT:    orq %rsi, %rdi
+; NOFASTLZCNT-NEXT:    sete %al
+; NOFASTLZCNT-NEXT:    orq %rcx, %rdx
+; NOFASTLZCNT-NEXT:    sete %cl
+; NOFASTLZCNT-NEXT:    orb %al, %cl
+; NOFASTLZCNT-NEXT:    movzbl %cl, %eax
+; NOFASTLZCNT-NEXT:    retq
+entry:
+  %a.sroa.2.0.insert.ext = zext i64 %a.coerce1 to i128
+  %a.sroa.2.0.insert.shift = shl nuw i128 %a.sroa.2.0.insert.ext, 64
+  %a.sroa.0.0.insert.ext = zext i64 %a.coerce0 to i128
+  %a.sroa.0.0.insert.insert = or i128 %a.sroa.2.0.insert.shift, %a.sroa.0.0.insert.ext
+  %b.sroa.2.0.insert.ext = zext i64 %b.coerce1 to i128
+  %b.sroa.2.0.insert.shift = shl nuw i128 %b.sroa.2.0.insert.ext, 64
+  %b.sroa.0.0.insert.ext = zext i64 %b.coerce0 to i128
+  %b.sroa.0.0.insert.insert = or i128 %b.sroa.2.0.insert.shift, %b.sroa.0.0.insert.ext
+  %cmp = icmp eq i128 %a.sroa.0.0.insert.insert, 0
+  %cmp3 = icmp eq i128 %b.sroa.0.0.insert.insert, 0
+  %0 = or i1 %cmp, %cmp3
+  %lor.ext = zext i1 %0 to i32
+  ret i32 %lor.ext
+}




More information about the llvm-commits mailing list