Skip to content

Commit 12c05ac

Browse files
committed
Refactor to simplify
Make the code simpler, shorter, and clearer by: * Splitting the lambda out into a separate function * Instead of returning an optional of a bool + value, use an enum + value * The new enum specifies whether to avoid optimizing, return LHS, return const, or return either. * Use the same enum to cover the logic of both the individual scalar elements, and the vector as a whole. * Use this enum to cover the fact that undef/poison could choose either side, rather than tracking it with a separate check. * Extract common code from the splat vs fixed vs scalar paths. * Merge undef handling with the above approach too, rather than using a separate check. * Simplify some comments.
1 parent 39c6a7f commit 12c05ac

File tree

1 file changed

+110
-121
lines changed

1 file changed

+110
-121
lines changed

llvm/lib/Analysis/InstructionSimplify.cpp

Lines changed: 110 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -6456,6 +6456,82 @@ static Value *foldMinimumMaximumSharedOp(Intrinsic::ID IID, Value *Op0,
64566456
return nullptr;
64576457
}
64586458

6459+
enum class MinMaxOptResult {
6460+
CannotOptimize = 0,
6461+
UseNewConstVal = 1,
6462+
UseOtherVal = 2,
6463+
// For undef/poison, we can choose to either propgate undef/poison or
6464+
// use the LHS value depending on what will allow more optimization.
6465+
UseEither = 3
6466+
};
6467+
// Get the optimized value for a min/max instruction with a single constant
6468+
// input (either undef or scalar constantFP). The result may indicate to
6469+
// use the non-const LHS value, use a new constant value instead (with NaNs
6470+
// quieted), or to choose either option in the case of undef/poison.
6471+
static MinMaxOptResult OptimizeConstMinMax(const Constant *RHSConst,
6472+
const Intrinsic::ID IID,
6473+
const CallBase *Call,
6474+
Constant **OutNewConstVal) {
6475+
assert(OutNewConstVal != nullptr);
6476+
6477+
bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
6478+
bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
6479+
bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
6480+
IID == Intrinsic::minimumnum;
6481+
6482+
// min/max(x, poison) -> either x or poison
6483+
if (isa<UndefValue>(RHSConst)) {
6484+
*OutNewConstVal = const_cast<Constant *>(RHSConst);
6485+
return MinMaxOptResult::UseEither;
6486+
}
6487+
6488+
const ConstantFP *CFP = dyn_cast<ConstantFP>(RHSConst);
6489+
if (!CFP)
6490+
return MinMaxOptResult::CannotOptimize;
6491+
APFloat CAPF = CFP->getValueAPF();
6492+
6493+
// minnum(x, qnan) -> x
6494+
// maxnum(x, qnan) -> x
6495+
// minnum(x, snan) -> qnan
6496+
// maxnum(x, snan) -> qnan
6497+
// minimum(X, nan) -> qnan
6498+
// maximum(X, nan) -> qnan
6499+
// minimumnum(X, nan) -> x
6500+
// maximumnum(X, nan) -> x
6501+
if (CAPF.isNaN()) {
6502+
if (PropagateNaN || (PropagateSNaN && CAPF.isSignaling())) {
6503+
*OutNewConstVal = ConstantFP::get(CFP->getType(), CAPF.makeQuiet());
6504+
return MinMaxOptResult::UseNewConstVal;
6505+
}
6506+
return MinMaxOptResult::UseOtherVal;
6507+
}
6508+
6509+
if (CAPF.isInfinity() || (Call && Call->hasNoInfs() && CAPF.isLargest())) {
6510+
// minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
6511+
// maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
6512+
// minimum(X, -inf) -> -inf if nnan
6513+
// maximum(X, +inf) -> +inf if nnan
6514+
// minimumnum(X, -inf) -> -inf
6515+
// maximumnum(X, +inf) -> +inf
6516+
if (CAPF.isNegative() == IsMin &&
6517+
(!PropagateNaN || (Call && Call->hasNoNaNs()))) {
6518+
*OutNewConstVal = const_cast<Constant *>(RHSConst);
6519+
return MinMaxOptResult::UseNewConstVal;
6520+
}
6521+
6522+
// minnum(X, +inf) -> X if nnan
6523+
// maxnum(X, -inf) -> X if nnan
6524+
// minimum(X, +inf) -> X (ignoring quieting of sNaNs)
6525+
// maximum(X, -inf) -> X (ignoring quieting of sNaNs)
6526+
// minimumnum(X, +inf) -> X if nnan
6527+
// maximumnum(X, -inf) -> X if nnan
6528+
if (CAPF.isNegative() != IsMin &&
6529+
(PropagateNaN || (Call && Call->hasNoNaNs())))
6530+
return MinMaxOptResult::UseOtherVal;
6531+
}
6532+
return MinMaxOptResult::CannotOptimize;
6533+
}
6534+
64596535
Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
64606536
Value *Op0, Value *Op1,
64616537
const SimplifyQuery &Q,
@@ -6721,142 +6797,55 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
67216797
if (isa<Constant>(Op0))
67226798
std::swap(Op0, Op1);
67236799

6724-
// If an argument is undef, return the other argument.
6725-
if (Q.isUndefValue(Op1))
6726-
return Op0;
6727-
67286800
if (Constant *C = dyn_cast<Constant>(Op1)) {
6729-
bool PropagateNaN =
6730-
IID == Intrinsic::minimum || IID == Intrinsic::maximum;
6731-
bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
6732-
bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
6733-
IID == Intrinsic::minimumnum;
6734-
6735-
// Get the optimized value for a constant scalar input. The result may
6736-
// indicate either to use the non-const LHS value, or return a pointer
6737-
// to a new constant value to use instead of the input (after e.g.
6738-
// quieting NaNs). Returns empty optional value if it cannot be optimized.
6739-
typedef struct {
6740-
bool UseNonConstVal;
6741-
Constant *NewConstVal;
6742-
} OptResult;
6743-
auto GetOptResultFor = [PropagateNaN, PropagateSNaN, IsMin,
6744-
Call](Constant *C) -> std::optional<OptResult> {
6745-
auto UseNonConstVal = []() -> OptResult { return {true, nullptr}; };
6746-
auto UseConstVal = [](Constant *C) -> OptResult { return {false, C}; };
6747-
6748-
// min/max(opt, poison) -> poison
6749-
if (isa<UndefValue>(C))
6750-
return UseConstVal(C);
6751-
6752-
const ConstantFP *CFP = dyn_cast<ConstantFP>(C);
6753-
if (!CFP)
6754-
return {};
6755-
APFloat CAPF = CFP->getValueAPF();
6756-
6757-
// minnum(x, qnan) -> x
6758-
// maxnum(x, qnan) -> x
6759-
// minnum(x, snan) -> qnan
6760-
// maxnum(x, snan) -> qnan
6761-
// minimum(X, nan) -> qnan
6762-
// maximum(X, nan) -> qnan
6763-
// minimumnum(X, nan) -> x
6764-
// maximumnum(X, nan) -> x
6765-
if (CAPF.isNaN()) {
6766-
if (PropagateNaN || (PropagateSNaN && CAPF.isSignaling()))
6767-
return UseConstVal(ConstantFP::get(C->getType(), CAPF.makeQuiet()));
6768-
else
6769-
return UseNonConstVal();
6770-
}
6771-
6772-
if (CAPF.isInfinity() ||
6773-
(Call && Call->hasNoInfs() && CAPF.isLargest())) {
6774-
// minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
6775-
// maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
6776-
// minimum(X, -inf) -> -inf if nnan
6777-
// maximum(X, +inf) -> +inf if nnan
6778-
// minimumnum(X, -inf) -> -inf
6779-
// maximumnum(X, +inf) -> +inf
6780-
if (CAPF.isNegative() == IsMin &&
6781-
(!PropagateNaN || (Call && Call->hasNoNaNs())))
6782-
return UseConstVal(C);
6783-
6784-
// minnum(X, +inf) -> X if nnan
6785-
// maxnum(X, -inf) -> X if nnan
6786-
// minimum(X, +inf) -> X (ignoring quieting of sNaNs)
6787-
// maximum(X, -inf) -> X (ignoring quieting of sNaNs)
6788-
// minimumnum(X, +inf) -> X if nnan
6789-
// maximumnum(X, -inf) -> X if nnan
6790-
if (CAPF.isNegative() != IsMin &&
6791-
(PropagateNaN || (Call && Call->hasNoNaNs())))
6792-
return UseNonConstVal();
6793-
}
6794-
6795-
// Cannot optimize this element
6796-
return {};
6797-
};
6801+
MinMaxOptResult OptResult = MinMaxOptResult::CannotOptimize;
6802+
Constant *NewConst = nullptr;
67986803

67996804
if (VectorType *VTy = dyn_cast<VectorType>(C->getType())) {
6800-
// Handle splat vectors (including scalable vectors)
6805+
ElementCount ElemCount = VTy->getElementCount();
6806+
68016807
if (Constant *SplatVal = C->getSplatValue()) {
6802-
std::optional<OptResult> OptSplatVal = GetOptResultFor(SplatVal);
6803-
if (OptSplatVal.has_value()) {
6804-
if (OptSplatVal.value().UseNonConstVal)
6805-
return Op0;
6806-
assert(OptSplatVal.value().NewConstVal != nullptr);
6807-
return ConstantVector::getSplat(VTy->getElementCount(),
6808-
OptSplatVal.value().NewConstVal);
6809-
}
6808+
// Handle splat vectors (including scalable vectors)
6809+
OptResult = OptimizeConstMinMax(SplatVal, IID, Call, &NewConst);
6810+
if (OptResult == MinMaxOptResult::UseNewConstVal)
6811+
NewConst = ConstantVector::getSplat(ElemCount, NewConst);
6812+
68106813
} else if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
6814+
// Storage to build up new const return value (with NaNs quieted)
6815+
SmallVector<Constant *, 16> NewC(ElemCount.getFixedValue());
6816+
68116817
// Check elementwise whether we can optimize to either a constant
68126818
// value or return the LHS value. We cannot mix and match LHS +
68136819
// constant elements, as this would require inserting a new
6814-
// VectorShuffle instruction, which is not allowed in simplifyBinOp,
6815-
// so bail early if any element cannot be optimized, or if lhs vs
6816-
// const optimizations start to mismatch. However, we can turn
6817-
// undef/poison into the LHS value, so only bail if we need at least 1
6818-
// non undef/poison RHS const.
6819-
bool CanOptimize = true;
6820-
bool AllConstValsAreUndef = true;
6821-
unsigned NumElts = FVty->getNumElements();
6822-
// Storage to build up the constant return value (possible altered
6823-
// from the input RHS value by quieting NaNs)
6824-
SmallVector<Constant *, 16> NewC(NumElts);
6825-
6826-
bool NeedsConstElement = false;
6827-
bool NeedsLHSElement = false;
6828-
for (unsigned i = 0; i != NumElts; ++i) {
6829-
Constant *EltC = C->getAggregateElement(i);
6830-
std::optional<OptResult> OptElemVal = GetOptResultFor(EltC);
6831-
if (!OptElemVal.has_value()) {
6832-
CanOptimize = false;
6820+
// VectorShuffle instruction, which is not allowed in simplifyBinOp.
6821+
OptResult = MinMaxOptResult::UseEither;
6822+
for (unsigned i = 0; i != ElemCount.getFixedValue(); ++i) {
6823+
auto ElemResult = OptimizeConstMinMax(C->getAggregateElement(i),
6824+
IID, Call, &NewConst);
6825+
if (ElemResult == MinMaxOptResult::CannotOptimize ||
6826+
(ElemResult != OptResult &&
6827+
OptResult != MinMaxOptResult::UseEither &&
6828+
ElemResult != MinMaxOptResult::UseEither)) {
6829+
OptResult = MinMaxOptResult::CannotOptimize;
68336830
break;
68346831
}
6835-
if (OptElemVal.value().UseNonConstVal) {
6836-
NeedsLHSElement = true;
6837-
if (NeedsConstElement && !AllConstValsAreUndef)
6838-
break;
6839-
} else {
6840-
NeedsConstElement = true;
6841-
assert(OptElemVal.value().NewConstVal != nullptr);
6842-
NewC[i] = OptElemVal.value().NewConstVal;
6843-
AllConstValsAreUndef &= isa<UndefValue>(NewC[i]);
6844-
if (NeedsLHSElement && !AllConstValsAreUndef)
6845-
break;
6846-
}
6832+
NewC[i] = NewConst;
6833+
if (ElemResult != MinMaxOptResult::UseEither)
6834+
OptResult = ElemResult;
68476835
}
6848-
6849-
if (CanOptimize && (!NeedsLHSElement || AllConstValsAreUndef))
6850-
return NeedsLHSElement ? Op0 : ConstantVector::get(NewC);
6836+
if (OptResult == MinMaxOptResult::UseNewConstVal)
6837+
NewConst = ConstantVector::get(NewC);
68516838
}
68526839
} else {
68536840
// Handle scalar inputs
6854-
std::optional<OptResult> OptScalarVal = GetOptResultFor(C);
6855-
if (OptScalarVal.has_value()) {
6856-
OptResult Res = OptScalarVal.value();
6857-
return Res.UseNonConstVal ? Op0 : Res.NewConstVal;
6858-
}
6841+
OptResult = OptimizeConstMinMax(C, IID, Call, &NewConst);
68596842
}
6843+
6844+
if (OptResult == MinMaxOptResult::UseOtherVal ||
6845+
OptResult == MinMaxOptResult::UseEither)
6846+
return Op0; // Return the other arg (ignoring NaN quieting)
6847+
else if (OptResult == MinMaxOptResult::UseNewConstVal)
6848+
return NewConst;
68606849
}
68616850

68626851
// Min/max of the same operation with common operand:

0 commit comments

Comments
 (0)