aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CodeGen/SelectOptimize.cpp
diff options
context:
space:
mode:
authorDavid Green <david.green@arm.com>2024-05-22 19:28:24 +0100
committerGitHub <noreply@github.com>2024-05-22 19:28:24 +0100
commit0a62a99aa610a7a8cf739208603646bc01b37347 (patch)
tree09ab9d84f1e01117135a92820eb04958a0b8afdf /llvm/lib/CodeGen/SelectOptimize.cpp
parent8baf96f3060bc26a308b3614feed4117e5299d3c (diff)
downloadllvm-0a62a99aa610a7a8cf739208603646bc01b37347.zip
llvm-0a62a99aa610a7a8cf739208603646bc01b37347.tar.gz
llvm-0a62a99aa610a7a8cf739208603646bc01b37347.tar.bz2
[SelectOpt] Add handling for not conditions. (#92517)
This patch attempts to help the SelectOpt pass detect select groups made up of conditions and not(conditions). Usually these are canonicalized in instcombine to remove the not and invert the true/false values, but this will not happen for Loginal operations, which can be beneficial to convert if they are part of a larger select group. The handling for not's are mostly handled in the SelectLike, which can be marked as Inverted in order to reverse the TrueValue and FalseValue. This helps fix a regression in fortran minloc constructs, after #84628 helped simplify a loop with branches into a loop with selects.
Diffstat (limited to 'llvm/lib/CodeGen/SelectOptimize.cpp')
-rw-r--r--llvm/lib/CodeGen/SelectOptimize.cpp82
1 files changed, 64 insertions, 18 deletions
diff --git a/llvm/lib/CodeGen/SelectOptimize.cpp b/llvm/lib/CodeGen/SelectOptimize.cpp
index 2e03ae6..0a5f0a8 100644
--- a/llvm/lib/CodeGen/SelectOptimize.cpp
+++ b/llvm/lib/CodeGen/SelectOptimize.cpp
@@ -130,7 +130,11 @@ public:
class SelectLike {
SelectLike(Instruction *I) : I(I) {}
+ /// The select (/or) instruction.
Instruction *I;
+ /// Whether this select is inverted, "not(cond), FalseVal, TrueVal", as
+ /// opposed to the original condition.
+ bool Inverted = false;
public:
/// Match a select or select-like instruction, returning a SelectLike.
@@ -153,14 +157,22 @@ public:
bool isValid() { return I; }
operator bool() { return isValid(); }
+ /// Invert the select by inverting the condition and switching the operands.
+ void setInverted() {
+ assert(!Inverted && "Trying to invert an inverted SelectLike");
+ assert(isa<Instruction>(getCondition()) &&
+ cast<Instruction>(getCondition())->getOpcode() ==
+ Instruction::Xor);
+ Inverted = true;
+ }
+ bool isInverted() const { return Inverted; }
+
Instruction *getI() { return I; }
const Instruction *getI() const { return I; }
Type *getType() const { return I->getType(); }
- /// Return the condition for the SelectLike instruction. For example the
- /// condition of a select or c in `or(zext(c), x)`
- Value *getCondition() const {
+ Value *getNonInvertedCondition() const {
if (auto *Sel = dyn_cast<SelectInst>(I))
return Sel->getCondition();
// Or(zext) case
@@ -177,11 +189,24 @@ public:
llvm_unreachable("Unhandled case in getCondition");
}
+ /// Return the condition for the SelectLike instruction. For example the
+ /// condition of a select or c in `or(zext(c), x)`
+ Value *getCondition() const {
+ Value *CC = getNonInvertedCondition();
+ // For inverted conditions the CC is checked when created to be a not
+ // (xor) instruction.
+ if (Inverted)
+ return cast<Instruction>(CC)->getOperand(0);
+ return CC;
+ }
+
/// Return the true value for the SelectLike instruction. Note this may not
/// exist for all SelectLike instructions. For example, for `or(zext(c), x)`
/// the true value would be `or(x,1)`. As this value does not exist, nullptr
/// is returned.
- Value *getTrueValue() const {
+ Value *getTrueValue(bool HonorInverts = true) const {
+ if (Inverted && HonorInverts)
+ return getFalseValue(/*HonorInverts=*/false);
if (auto *Sel = dyn_cast<SelectInst>(I))
return Sel->getTrueValue();
// Or(zext) case - The true value is Or(X), so return nullptr as the value
@@ -195,7 +220,9 @@ public:
/// Return the false value for the SelectLike instruction. For example the
/// getFalseValue of a select or `x` in `or(zext(c), x)` (which is
/// `select(c, x|1, x)`)
- Value *getFalseValue() const {
+ Value *getFalseValue(bool HonorInverts = true) const {
+ if (Inverted && HonorInverts)
+ return getTrueValue(/*HonorInverts=*/false);
if (auto *Sel = dyn_cast<SelectInst>(I))
return Sel->getFalseValue();
// Or(zext) case - return the operand which is not the zext.
@@ -216,8 +243,8 @@ public:
/// InstCostMap. This may need to be generated for select-like instructions.
Scaled64 getTrueOpCost(DenseMap<const Instruction *, CostInfo> &InstCostMap,
const TargetTransformInfo *TTI) {
- if (auto *Sel = dyn_cast<SelectInst>(I))
- if (auto *I = dyn_cast<Instruction>(Sel->getTrueValue()))
+ if (isa<SelectInst>(I))
+ if (auto *I = dyn_cast<Instruction>(getTrueValue()))
return InstCostMap.contains(I) ? InstCostMap[I].NonPredCost
: Scaled64::getZero();
@@ -242,8 +269,8 @@ public:
Scaled64
getFalseOpCost(DenseMap<const Instruction *, CostInfo> &InstCostMap,
const TargetTransformInfo *TTI) {
- if (auto *Sel = dyn_cast<SelectInst>(I))
- if (auto *I = dyn_cast<Instruction>(Sel->getFalseValue()))
+ if (isa<SelectInst>(I))
+ if (auto *I = dyn_cast<Instruction>(getFalseValue()))
return InstCostMap.contains(I) ? InstCostMap[I].NonPredCost
: Scaled64::getZero();
@@ -510,9 +537,10 @@ getTrueOrFalseValue(SelectOptimizeImpl::SelectLike SI, bool isTrue,
for (SelectInst *DefSI = dyn_cast<SelectInst>(SI.getI());
DefSI != nullptr && Selects.count(DefSI);
DefSI = dyn_cast<SelectInst>(V)) {
- assert(DefSI->getCondition() == SI.getCondition() &&
- "The condition of DefSI does not match with SI");
- V = (isTrue ? DefSI->getTrueValue() : DefSI->getFalseValue());
+ if (DefSI->getCondition() == SI.getCondition())
+ V = (isTrue ? DefSI->getTrueValue() : DefSI->getFalseValue());
+ else // Handle inverted SI
+ V = (!isTrue ? DefSI->getTrueValue() : DefSI->getFalseValue());
}
if (isa<BinaryOperator>(SI.getI())) {
@@ -632,18 +660,19 @@ void SelectOptimizeImpl::convertProfitableSIGroups(SelectGroups &ProfSIGroups) {
// Delete the unconditional branch that was just created by the split.
StartBlock->getTerminator()->eraseFromParent();
- // Move any debug/pseudo instructions that were in-between the select
- // group to the newly-created end block.
- SmallVector<Instruction *, 2> DebugPseudoINS;
+ // Move any debug/pseudo instructions and not's that were in-between the
+ // select group to the newly-created end block.
+ SmallVector<Instruction *, 2> SinkInstrs;
auto DIt = SI.getI()->getIterator();
while (&*DIt != LastSI.getI()) {
if (DIt->isDebugOrPseudoInst())
- DebugPseudoINS.push_back(&*DIt);
+ SinkInstrs.push_back(&*DIt);
+ if (match(&*DIt, m_Not(m_Specific(SI.getCondition()))))
+ SinkInstrs.push_back(&*DIt);
DIt++;
}
- for (auto *DI : DebugPseudoINS) {
+ for (auto *DI : SinkInstrs)
DI->moveBeforePreserving(&*EndBlock->getFirstInsertionPt());
- }
// Duplicate implementation for DbgRecords, the non-instruction debug-info
// format. Helper lambda for moving DbgRecords to the end block.
@@ -765,6 +794,13 @@ void SelectOptimizeImpl::collectSelectGroups(BasicBlock &BB,
++BBIt;
continue;
}
+
+ // Skip not(select(..)), if the not is part of the same select group
+ if (match(NI, m_Not(m_Specific(SI.getCondition())))) {
+ ++BBIt;
+ continue;
+ }
+
// We only allow selects in the same group, not other select-like
// instructions.
if (!isa<SelectInst>(NI))
@@ -773,6 +809,10 @@ void SelectOptimizeImpl::collectSelectGroups(BasicBlock &BB,
SelectLike NSI = SelectLike::match(NI);
if (NSI && SI.getCondition() == NSI.getCondition()) {
SIGroup.push_back(NSI);
+ } else if (NSI && match(NSI.getCondition(),
+ m_Not(m_Specific(SI.getCondition())))) {
+ NSI.setInverted();
+ SIGroup.push_back(NSI);
} else
break;
++BBIt;
@@ -783,6 +823,12 @@ void SelectOptimizeImpl::collectSelectGroups(BasicBlock &BB,
if (!isSelectKindSupported(SI))
continue;
+ LLVM_DEBUG({
+ dbgs() << "New Select group with\n";
+ for (auto SI : SIGroup)
+ dbgs() << " " << *SI.getI() << "\n";
+ });
+
SIGroups.push_back(SIGroup);
}
}