-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[InstCombine] Add folds for (fp_binop ({s|u}itofp x), ({s|u}itofp y))
#82555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5191b03
63bb759
0e4d930
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1401,6 +1401,179 @@ Value *InstCombinerImpl::dyn_castNegVal(Value *V) const { | |||||
return nullptr; | ||||||
} | ||||||
|
||||||
// Try to fold: | ||||||
// 1) (fp_binop ({s|u}itofp x), ({s|u}itofp y)) | ||||||
// -> ({s|u}itofp (int_binop x, y)) | ||||||
// 2) (fp_binop ({s|u}itofp x), FpC) | ||||||
// -> ({s|u}itofp (int_binop x, (fpto{s|u}i FpC))) | ||||||
Instruction *InstCombinerImpl::foldFBinOpOfIntCasts(BinaryOperator &BO) { | ||||||
Value *IntOps[2] = {nullptr, nullptr}; | ||||||
Constant *Op1FpC = nullptr; | ||||||
|
||||||
// Check for: | ||||||
// 1) (binop ({s|u}itofp x), ({s|u}itofp y)) | ||||||
// 2) (binop ({s|u}itofp x), FpC) | ||||||
if (!match(BO.getOperand(0), m_SIToFP(m_Value(IntOps[0]))) && | ||||||
!match(BO.getOperand(0), m_UIToFP(m_Value(IntOps[0])))) | ||||||
return nullptr; | ||||||
|
||||||
if (!match(BO.getOperand(1), m_Constant(Op1FpC)) && | ||||||
!match(BO.getOperand(1), m_SIToFP(m_Value(IntOps[1]))) && | ||||||
!match(BO.getOperand(1), m_UIToFP(m_Value(IntOps[1])))) | ||||||
return nullptr; | ||||||
|
||||||
Type *FPTy = BO.getType(); | ||||||
Type *IntTy = IntOps[0]->getType(); | ||||||
|
||||||
// Do we have signed casts? | ||||||
bool OpsFromSigned = isa<SIToFPInst>(BO.getOperand(0)); | ||||||
|
||||||
unsigned IntSz = IntTy->getScalarSizeInBits(); | ||||||
// This is the maximum number of inuse bits by the integer where the int -> fp | ||||||
// casts are exact. | ||||||
unsigned MaxRepresentableBits = | ||||||
APFloat::semanticsPrecision(FPTy->getScalarType()->getFltSemantics()); | ||||||
|
||||||
// Cache KnownBits a bit to potentially save some analysis. | ||||||
WithCache<const Value *> OpsKnown[2] = {IntOps[0], IntOps[1]}; | ||||||
|
||||||
// Preserve known number of leading bits. This can allow us to trivial nsw/nuw | ||||||
// checks later on. | ||||||
unsigned NumUsedLeadingBits[2] = {IntSz, IntSz}; | ||||||
|
||||||
auto IsNonZero = [&](unsigned OpNo) -> bool { | ||||||
if (OpsKnown[OpNo].hasKnownBits() && | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think we want to do this. We may unnecessarily compute knownbits. The idea |
||||||
OpsKnown[OpNo].getKnownBits(SQ).isNonZero()) | ||||||
return true; | ||||||
return isKnownNonZero(IntOps[OpNo], SQ.DL); | ||||||
}; | ||||||
|
||||||
auto IsNonNeg = [&](unsigned OpNo) -> bool { | ||||||
if (OpsKnown[OpNo].hasKnownBits() && | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
OpsKnown[OpNo].getKnownBits(SQ).isNonNegative()) | ||||||
return true; | ||||||
return isKnownNonNegative(IntOps[OpNo], SQ); | ||||||
}; | ||||||
|
||||||
// Check if we know for certain that ({s|u}itofp op) is exact. | ||||||
auto IsValidPromotion = [&](unsigned OpNo) -> bool { | ||||||
// If fp precision >= bitwidth(op) then its exact. | ||||||
// NB: This is slightly conservative for `sitofp`. For signed conversion, we | ||||||
// can handle `MaxRepresentableBits == IntSz - 1` as the sign bit will be | ||||||
// handled specially. We can't, however, increase the bound arbitrarily for | ||||||
// `sitofp` as for larger sizes, it won't sign extend. | ||||||
if (MaxRepresentableBits < IntSz) { | ||||||
// Otherwise if its signed cast check that fp precisions >= bitwidth(op) - | ||||||
// numSignBits(op). | ||||||
// TODO: If we add support for `WithCache` in `ComputeNumSignBits`, change | ||||||
// `IntOps[OpNo]` arguments to `KnownOps[OpNo]`. | ||||||
if (OpsFromSigned) | ||||||
NumUsedLeadingBits[OpNo] = IntSz - ComputeNumSignBits(IntOps[OpNo]); | ||||||
// Finally for unsigned check that fp precision >= bitwidth(op) - | ||||||
// numLeadingZeros(op). | ||||||
else { | ||||||
NumUsedLeadingBits[OpNo] = | ||||||
IntSz - OpsKnown[OpNo].getKnownBits(SQ).countMinLeadingZeros(); | ||||||
} | ||||||
} | ||||||
// NB: We could also check if op is known to be a power of 2 or zero (which | ||||||
// will always be representable). Its unlikely, however, that is we are | ||||||
// unable to bound op in any way we will be able to pass the overflow checks | ||||||
// later on. | ||||||
|
||||||
if (MaxRepresentableBits < NumUsedLeadingBits[OpNo]) | ||||||
return false; | ||||||
// Signed + Mul also requires that op is non-zero to avoid -0 cases. | ||||||
return !OpsFromSigned || BO.getOpcode() != Instruction::FMul || | ||||||
IsNonZero(OpNo); | ||||||
}; | ||||||
|
||||||
// If we have a constant rhs, see if we can losslessly convert it to an int. | ||||||
if (Op1FpC != nullptr) { | ||||||
Constant *Op1IntC = ConstantFoldCastOperand( | ||||||
OpsFromSigned ? Instruction::FPToSI : Instruction::FPToUI, Op1FpC, | ||||||
IntTy, DL); | ||||||
if (Op1IntC == nullptr) | ||||||
return nullptr; | ||||||
if (ConstantFoldCastOperand(OpsFromSigned ? Instruction::SIToFP | ||||||
: Instruction::UIToFP, | ||||||
Op1IntC, FPTy, DL) != Op1FpC) | ||||||
return nullptr; | ||||||
|
||||||
// First try to keep sign of cast the same. | ||||||
IntOps[1] = Op1IntC; | ||||||
} | ||||||
|
||||||
// Ensure lhs/rhs integer types match. | ||||||
if (IntTy != IntOps[1]->getType()) | ||||||
return nullptr; | ||||||
|
||||||
if (Op1FpC == nullptr) { | ||||||
if (OpsFromSigned != isa<SIToFPInst>(BO.getOperand(1))) { | ||||||
// If we have a signed + unsigned, see if we can treat both as signed | ||||||
// (uitofp nneg x) == (sitofp nneg x). | ||||||
if (OpsFromSigned ? !IsNonNeg(1) : !IsNonNeg(0)) | ||||||
dtcxzyw marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return nullptr; | ||||||
OpsFromSigned = true; | ||||||
} | ||||||
if (!IsValidPromotion(1)) | ||||||
return nullptr; | ||||||
} | ||||||
if (!IsValidPromotion(0)) | ||||||
return nullptr; | ||||||
|
||||||
// Final we check if the integer version of the binop will not overflow. | ||||||
BinaryOperator::BinaryOps IntOpc; | ||||||
// Because of the precision check, we can often rule out overflows. | ||||||
bool NeedsOverflowCheck = true; | ||||||
// Try to conservatively rule out overflow based on the already done precision | ||||||
// checks. | ||||||
unsigned OverflowMaxOutputBits = OpsFromSigned ? 2 : 1; | ||||||
unsigned OverflowMaxCurBits = | ||||||
std::max(NumUsedLeadingBits[0], NumUsedLeadingBits[1]); | ||||||
bool OutputSigned = OpsFromSigned; | ||||||
switch (BO.getOpcode()) { | ||||||
case Instruction::FAdd: | ||||||
IntOpc = Instruction::Add; | ||||||
OverflowMaxOutputBits += OverflowMaxCurBits; | ||||||
break; | ||||||
case Instruction::FSub: | ||||||
IntOpc = Instruction::Sub; | ||||||
OverflowMaxOutputBits += OverflowMaxCurBits; | ||||||
break; | ||||||
case Instruction::FMul: | ||||||
IntOpc = Instruction::Mul; | ||||||
OverflowMaxOutputBits += OverflowMaxCurBits * 2; | ||||||
break; | ||||||
default: | ||||||
llvm_unreachable("Unsupported binop"); | ||||||
} | ||||||
// The precision check may have already ruled out overflow. | ||||||
if (OverflowMaxOutputBits < IntSz) { | ||||||
NeedsOverflowCheck = false; | ||||||
// We can bound unsigned overflow from sub to in range signed value (this is | ||||||
// what allows us to avoid the overflow check for sub). | ||||||
if (IntOpc == Instruction::Sub) | ||||||
OutputSigned = true; | ||||||
} | ||||||
|
||||||
// Precision check did not rule out overflow, so need to check. | ||||||
// TODO: If we add support for `WithCache` in `willNotOverflow`, change | ||||||
// `IntOps[...]` arguments to `KnownOps[...]`. | ||||||
if (NeedsOverflowCheck && | ||||||
!willNotOverflow(IntOpc, IntOps[0], IntOps[1], BO, OutputSigned)) | ||||||
return nullptr; | ||||||
|
||||||
Value *IntBinOp = Builder.CreateBinOp(IntOpc, IntOps[0], IntOps[1]); | ||||||
dtcxzyw marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
if (auto *IntBO = dyn_cast<BinaryOperator>(IntBinOp)) { | ||||||
IntBO->setHasNoSignedWrap(OutputSigned); | ||||||
IntBO->setHasNoUnsignedWrap(!OutputSigned); | ||||||
} | ||||||
if (OutputSigned) | ||||||
return new SIToFPInst(IntBinOp, FPTy); | ||||||
return new UIToFPInst(IntBinOp, FPTy); | ||||||
} | ||||||
|
||||||
/// A binop with a constant operand and a sign-extended boolean operand may be | ||||||
/// converted into a select of constants by applying the binary operation to | ||||||
/// the constant with the two possible values of the extended boolean (0 or -1). | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off by 1 depending on signed or unsigned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we are overly conservative if
IntSz == MaxRepresentableBits + 1
and its signed (not we can't expand the bound for saysitofp i16 to half
b.c it won't sign extend). So for things likei12
,i24
, ...I'll add comment to the affect, although don't think its really worth increase code complexity to handle a special case that will almost never actually apply (weird integer widths are rare).
Proofs:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't add the comment here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added it at the check vs
IntSz
inisValidPromotion
.