diff options
author | Igor Kirillov <igor.kirillov@arm.com> | 2024-12-12 14:06:24 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-12 14:06:24 +0000 |
commit | e909c0ccd40e6d6aa2d10e0b60e8b992f3cde35b (patch) | |
tree | f912b68ea045dcb6e24e2cfc5659f809a34f7760 /llvm/lib/CodeGen/SelectOptimize.cpp | |
parent | 10ef20f6a629797d81252de143117e2a0bc6556d (diff) | |
download | llvm-e909c0ccd40e6d6aa2d10e0b60e8b992f3cde35b.zip llvm-e909c0ccd40e6d6aa2d10e0b60e8b992f3cde35b.tar.gz llvm-e909c0ccd40e6d6aa2d10e0b60e8b992f3cde35b.tar.bz2 |
[SelectOpt] Add support for AShr/LShr operands (#118495)
For conditional increments with sign check conditions like X < 0 or X >= 0,
the compiler may generate code like this:
%cmp = icmp sgt i64 %1, -1
%shift = ashr i64 %1, 63
%j.next = add nsw i64 %j, %shift
%sel = select i1 %cmp ...
, where %cmp is not in computation but in some other implicit or regular
expressions. This patch allows SelectOptimize pass to recognise these
cases.
Diffstat (limited to 'llvm/lib/CodeGen/SelectOptimize.cpp')
-rw-r--r-- | llvm/lib/CodeGen/SelectOptimize.cpp | 90 |
1 files changed, 66 insertions, 24 deletions
diff --git a/llvm/lib/CodeGen/SelectOptimize.cpp b/llvm/lib/CodeGen/SelectOptimize.cpp index 2ec5af2d..98321a3 100644 --- a/llvm/lib/CodeGen/SelectOptimize.cpp +++ b/llvm/lib/CodeGen/SelectOptimize.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/SelectOptimize.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/BlockFrequencyInfo.h" @@ -218,7 +219,7 @@ public: private: // Select groups consist of consecutive select-like instructions with the same // condition. Between select-likes could be any number of auxiliary - // instructions related to the condition like not, zext + // instructions related to the condition like not, zext, ashr/lshr struct SelectGroup { Value *Condition; SmallVector<SelectLike, 2> Selects; @@ -496,7 +497,13 @@ static Value *getTrueOrFalseValue( auto *CBO = BO->clone(); auto CondIdx = SI.getConditionOpIndex(); - CBO->setOperand(CondIdx, ConstantInt::get(CBO->getType(), 1)); + auto *AuxI = cast<Instruction>(CBO->getOperand(CondIdx)); + if (isa<ZExtInst>(AuxI) || isa<LShrOperator>(AuxI)) { + CBO->setOperand(CondIdx, ConstantInt::get(CBO->getType(), 1)); + } else { + assert(isa<AShrOperator>(AuxI) && "Unexpected opcode"); + CBO->setOperand(CondIdx, ConstantInt::get(CBO->getType(), -1)); + } unsigned OtherIdx = 1 - CondIdx; if (auto *IV = dyn_cast<Instruction>(CBO->getOperand(OtherIdx))) { @@ -755,6 +762,9 @@ void SelectOptimizeImpl::collectSelectGroups(BasicBlock &BB, // zero or some constant value on True/False branch, such as: // * ZExt(1bit) // * Not(1bit) + // * A(L)Shr(Val), ValBitSize - 1, where there is a condition like `Val <= 0` + // earlier in the BB. For conditions that check the sign of the Val compiler + // may generate shifts instead of ZExt/SExt. struct SelectLikeInfo { Value *Cond; bool IsAuxiliary; @@ -763,11 +773,19 @@ void SelectOptimizeImpl::collectSelectGroups(BasicBlock &BB, }; DenseMap<Value *, SelectLikeInfo> SelectInfo; + // Keeps visited comparisons to help identify AShr/LShr variants of auxiliary + // instructions. + SmallSetVector<CmpInst *, 4> SeenCmp; // Check if the instruction is SelectLike or might be part of SelectLike // expression, put information into SelectInfo and return the iterator to the // inserted position. - auto ProcessSelectInfo = [&SelectInfo](Instruction *I) { + auto ProcessSelectInfo = [&SelectInfo, &SeenCmp](Instruction *I) { + if (auto *Cmp = dyn_cast<CmpInst>(I)) { + SeenCmp.insert(Cmp); + return SelectInfo.end(); + } + Value *Cond; if (match(I, m_OneUse(m_ZExt(m_Value(Cond)))) && Cond->getType()->isIntegerTy(1)) { @@ -784,35 +802,59 @@ void SelectOptimizeImpl::collectSelectGroups(BasicBlock &BB, bool Inverted = match(Cond, m_Not(m_Value(Cond))); return SelectInfo.insert({I, {Cond, false, Inverted, 0}}).first; } + Value *Val; + ConstantInt *Shift; + if (match(I, m_Shr(m_Value(Val), m_ConstantInt(Shift))) && + I->getType()->getIntegerBitWidth() == Shift->getZExtValue() + 1) { + for (auto *CmpI : SeenCmp) { + auto Pred = CmpI->getPredicate(); + if (Val != CmpI->getOperand(0)) + continue; + if ((Pred == CmpInst::ICMP_SGT && + match(CmpI->getOperand(1), m_ConstantInt<-1>())) || + (Pred == CmpInst::ICMP_SGE && + match(CmpI->getOperand(1), m_Zero())) || + (Pred == CmpInst::ICMP_SLT && + match(CmpI->getOperand(1), m_Zero())) || + (Pred == CmpInst::ICMP_SLE && + match(CmpI->getOperand(1), m_ConstantInt<-1>()))) { + bool Inverted = + Pred == CmpInst::ICMP_SGT || Pred == CmpInst::ICMP_SGE; + return SelectInfo.insert({I, {CmpI, true, Inverted, 0}}).first; + } + } + return SelectInfo.end(); + } - // An Or(zext(i1 X), Y) can also be treated like a select, with condition X + // An BinOp(Aux(X), Y) can also be treated like a select, with condition X // and values Y|1 and Y. - if (auto *BO = dyn_cast<BinaryOperator>(I)) { - switch (I->getOpcode()) { - case Instruction::Add: - case Instruction::Sub: { - Value *X; - if (!((PatternMatch::match(I->getOperand(0), - m_OneUse(m_ZExt(m_Value(X)))) || - PatternMatch::match(I->getOperand(1), - m_OneUse(m_ZExt(m_Value(X))))) && - X->getType()->isIntegerTy(1))) - return SelectInfo.end(); - break; - } - case Instruction::Or: - if (BO->getType()->isIntegerTy(1) || BO->getOpcode() != Instruction::Or) - return SelectInfo.end(); - break; - } + // `Aux` can be either `ZExt(1bit)` or `XShr(Val), ValBitSize - 1` + // `BinOp` can be Add, Sub, Or + Value *X; + auto MatchZExtPattern = m_c_BinOp(m_Value(), m_OneUse(m_ZExt(m_Value(X)))); + auto MatchShiftPattern = + m_c_BinOp(m_Value(), m_OneUse(m_Shr(m_Value(X), m_ConstantInt(Shift)))); + + // This check is unnecessary, but it prevents costly access to the + // SelectInfo map. + if ((match(I, MatchZExtPattern) && X->getType()->isIntegerTy(1)) || + (match(I, MatchShiftPattern) && + X->getType()->getIntegerBitWidth() == Shift->getZExtValue() + 1)) { + if (I->getOpcode() != Instruction::Add && + I->getOpcode() != Instruction::Sub && + I->getOpcode() != Instruction::Or) + return SelectInfo.end(); + + if (I->getOpcode() == Instruction::Or && I->getType()->isIntegerTy(1)) + return SelectInfo.end(); // Iterate through operands and find dependant on recognised sign // extending auxiliary select-like instructions. The operand index does // not matter for Add and Or. However, for Sub, we can only safely // transform when the operand is second. - unsigned Idx = BO->getOpcode() == Instruction::Sub ? 1 : 0; + unsigned Idx = I->getOpcode() == Instruction::Sub ? 1 : 0; for (; Idx < 2; Idx++) { - auto *Op = BO->getOperand(Idx); + auto *Op = I->getOperand(Idx); auto It = SelectInfo.find(Op); if (It != SelectInfo.end() && It->second.IsAuxiliary) { Cond = It->second.Cond; |