[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