[llvm] [InstCombine] Avoid DeMorgan's on occasion (PR #109215)

Miguel Saldivar via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 18 15:55:25 PDT 2024


https://github.com/Saldivarcher created https://github.com/llvm/llvm-project/pull/109215

When `andn` is available, we should avoid switching `s &= ~(z & ~y);` into `s &= ~z | y;`

This patch turns this assembly from:
```
foo:
        not     rcx
        and     rsi, rdx
        andn    rax, rsi, rdi
        or      rcx, rdx
        and     rax, rcx
        ret
```
into:
```
foo:
        and     rsi, rdx
        andn    rcx, rdx, rcx
        andn    rax, rsi, rdi
        andn    rax, rcx, rax
        ret
```

>From 6621234f90687e803614367a3c672b3a20f97b50 Mon Sep 17 00:00:00 2001
From: Miguel Saldivar <miguel.saldivar at hpe.com>
Date: Wed, 18 Sep 2024 17:51:09 -0500
Subject: [PATCH] [InstCombine] Avoid DeMorgan's on occasion

When `andn` is available, we should avoid switching `s &= ~(z & ~y);`
into `s &= ~z | y;`

This patch turns this assembly from:
```
foo:
        not     rcx
        and     rsi, rdx
        andn    rax, rsi, rdi
        or      rcx, rdx
        and     rax, rcx
        ret
```
into:
```
foo:
        and     rsi, rdx
        andn    rcx, rdx, rcx
        andn    rax, rsi, rdi
        andn    rax, rcx, rax
        ret
```
---
 .../llvm/Analysis/TargetTransformInfo.h       |  6 +++++
 .../llvm/Analysis/TargetTransformInfoImpl.h   |  2 ++
 .../Transforms/InstCombine/InstCombiner.h     |  1 +
 llvm/lib/Analysis/TargetTransformInfo.cpp     |  4 ++++
 .../lib/Target/X86/X86TargetTransformInfo.cpp | 22 +++++++++++++++++++
 llvm/lib/Target/X86/X86TargetTransformInfo.h  |  1 +
 .../InstCombine/InstCombineAndOrXor.cpp       | 11 ++++++----
 .../InstCombine/InstructionCombining.cpp      |  2 ++
 8 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index b2124c6106198e..01375052da5a4e 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -823,6 +823,8 @@ class TargetTransformInfo {
   /// would typically be allowed using throughput or size cost models.
   bool hasDivRemOp(Type *DataType, bool IsSigned) const;
 
+  bool hasAndNot(Type *DataType) const;
+
   /// Return true if the given instruction (assumed to be a memory access
   /// instruction) has a volatile variant. If that's the case then we can avoid
   /// addrspacecast to generic AS for volatile loads/stores. Default
@@ -1912,6 +1914,7 @@ class TargetTransformInfo::Concept {
                                const SmallBitVector &OpcodeMask) const = 0;
   virtual bool enableOrderedReductions() = 0;
   virtual bool hasDivRemOp(Type *DataType, bool IsSigned) = 0;
+  virtual bool hasAndNot(Type *DataType) = 0;
   virtual bool hasVolatileVariant(Instruction *I, unsigned AddrSpace) = 0;
   virtual bool prefersVectorizedAddressing() = 0;
   virtual InstructionCost getScalingFactorCost(Type *Ty, GlobalValue *BaseGV,
@@ -2430,6 +2433,9 @@ class TargetTransformInfo::Model final : public TargetTransformInfo::Concept {
   bool hasDivRemOp(Type *DataType, bool IsSigned) override {
     return Impl.hasDivRemOp(DataType, IsSigned);
   }
+
+  bool hasAndNot(Type *DataType) override { return Impl.hasAndNot(DataType); }
+
   bool hasVolatileVariant(Instruction *I, unsigned AddrSpace) override {
     return Impl.hasVolatileVariant(I, AddrSpace);
   }
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index 90eef93a2a54d5..49109d975d867b 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -335,6 +335,8 @@ class TargetTransformInfoImplBase {
 
   bool hasDivRemOp(Type *DataType, bool IsSigned) const { return false; }
 
+  bool hasAndNot(Type *DataType) const { return false; }
+
   bool hasVolatileVariant(Instruction *I, unsigned AddrSpace) const {
     return false;
   }
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index f5f16037bef893..f54b71786c60e4 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -526,6 +526,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
                              bool AllowMultipleUsers = false) = 0;
 
   bool isValidAddrSpaceCast(unsigned FromAS, unsigned ToAS) const;
+  bool hasAndNot(Type *DT) const;
 };
 
 } // namespace llvm
diff --git a/llvm/lib/Analysis/TargetTransformInfo.cpp b/llvm/lib/Analysis/TargetTransformInfo.cpp
index 2c26493bd3f1ca..682fea7cda8502 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -530,6 +530,10 @@ bool TargetTransformInfo::hasDivRemOp(Type *DataType, bool IsSigned) const {
   return TTIImpl->hasDivRemOp(DataType, IsSigned);
 }
 
+bool TargetTransformInfo::hasAndNot(Type *DataType) const {
+  return TTIImpl->hasAndNot(DataType);
+}
+
 bool TargetTransformInfo::hasVolatileVariant(Instruction *I,
                                              unsigned AddrSpace) const {
   return TTIImpl->hasVolatileVariant(I, AddrSpace);
diff --git a/llvm/lib/Target/X86/X86TargetTransformInfo.cpp b/llvm/lib/Target/X86/X86TargetTransformInfo.cpp
index cb9ee64a677a7e..c3917edddf8804 100644
--- a/llvm/lib/Target/X86/X86TargetTransformInfo.cpp
+++ b/llvm/lib/Target/X86/X86TargetTransformInfo.cpp
@@ -6250,6 +6250,28 @@ bool X86TTIImpl::hasDivRemOp(Type *DataType, bool IsSigned) {
   return TLI->isOperationLegal(IsSigned ? ISD::SDIVREM : ISD::UDIVREM, VT);
 }
 
+bool X86TTIImpl::hasAndNot(Type *DataType) {
+  EVT VT = TLI->getValueType(DL, DataType);
+
+  if (VT.isVector()) {
+    if (!ST->hasSSE1() || VT.getSizeInBits() < 128)
+      return false;
+
+    if (VT == MVT::v4i32)
+      return false;
+
+    return ST->hasSSE2();
+  }
+
+  if (!ST->hasBMI())
+    return false;
+
+  if (VT != MVT::i32 && VT != MVT::i64)
+    return false;
+
+  return true;
+}
+
 bool X86TTIImpl::isExpensiveToSpeculativelyExecute(const Instruction* I) {
   // FDIV is always expensive, even if it has a very low uop count.
   // TODO: Still necessary for recent CPUs with low latency/throughput fdiv?
diff --git a/llvm/lib/Target/X86/X86TargetTransformInfo.h b/llvm/lib/Target/X86/X86TargetTransformInfo.h
index b619090e8e1e01..dfb9b5aa0ce58a 100644
--- a/llvm/lib/Target/X86/X86TargetTransformInfo.h
+++ b/llvm/lib/Target/X86/X86TargetTransformInfo.h
@@ -277,6 +277,7 @@ class X86TTIImpl : public BasicTTIImplBase<X86TTIImpl> {
   bool isLegalAltInstr(VectorType *VecTy, unsigned Opcode0, unsigned Opcode1,
                        const SmallBitVector &OpcodeMask) const;
   bool hasDivRemOp(Type *DataType, bool IsSigned);
+  bool hasAndNot(Type *DataType);
   bool isExpensiveToSpeculativelyExecute(const Instruction *I);
   bool isFCmpOrdCheaperThanFCmpZero(Type *Ty);
   bool areInlineCompatible(const Function *Caller,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 4f557532f9f783..27209f5b37e01c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2630,8 +2630,9 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   if (Instruction *FoldedLogic = foldBinOpIntoSelectOrPhi(I))
     return FoldedLogic;
 
-  if (Instruction *DeMorgan = matchDeMorgansLaws(I, *this))
-    return DeMorgan;
+  if (!hasAndNot(I.getType()))
+    if (Instruction *DeMorgan = matchDeMorgansLaws(I, *this))
+      return DeMorgan;
 
   {
     Value *A, *B, *C;
@@ -4486,11 +4487,13 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
   //       those are handled via SimplifySelectsFeedingBinaryOp().
   Type *Ty = I.getType();
   Value *X, *Y;
-  if (match(NotOp, m_OneUse(m_c_And(m_Not(m_Value(X)), m_Value(Y))))) {
+  if (match(NotOp, m_OneUse(m_c_And(m_Not(m_Value(X)), m_Value(Y)))) &&
+      !hasAndNot(Ty)) {
     Value *NotY = Builder.CreateNot(Y, Y->getName() + ".not");
     return BinaryOperator::CreateOr(X, NotY);
   }
-  if (match(NotOp, m_OneUse(m_LogicalAnd(m_Not(m_Value(X)), m_Value(Y))))) {
+  if (match(NotOp, m_OneUse(m_LogicalAnd(m_Not(m_Value(X)), m_Value(Y)))) &&
+      !hasAndNot(Ty)) {
     Value *NotY = Builder.CreateNot(Y, Y->getName() + ".not");
     return SelectInst::Create(X, ConstantInt::getTrue(Ty), NotY);
   }
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 8195e0539305cc..6147f3a7fb99d9 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -189,6 +189,8 @@ bool InstCombiner::isValidAddrSpaceCast(unsigned FromAS, unsigned ToAS) const {
   return TTI.isValidAddrSpaceCast(FromAS, ToAS);
 }
 
+bool InstCombiner::hasAndNot(Type *DT) const { return TTI.hasAndNot(DT); }
+
 Value *InstCombinerImpl::EmitGEPOffset(GEPOperator *GEP, bool RewriteGEP) {
   if (!RewriteGEP)
     return llvm::emitGEPOffset(&Builder, DL, GEP);



More information about the llvm-commits mailing list