forked from OSchip/llvm-project
[InstCombine] Refactor out OptimizeOverflowCheck. NFCI.
Summary: This patch adds an enum `OverflowCheckFlavor` and a function `OptimizeOverflowCheck`. This will allow InstCombine to optimize overflow checks without directly introducing an intermediate call to the `llvm.$op.with.overflow` instrinsics. This specific change is a refactoring and does not intend to change behavior. Reviewers: majnemer, atrick Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D8888 llvm-svn: 234388
This commit is contained in:
parent
e09a928c80
commit
b098447128
|
@ -415,112 +415,35 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Intrinsic::uadd_with_overflow: {
|
|
||||||
Value *LHS = II->getArgOperand(0), *RHS = II->getArgOperand(1);
|
case Intrinsic::uadd_with_overflow: // FALLTHROUGH
|
||||||
OverflowResult OR = computeOverflowForUnsignedAdd(LHS, RHS, II);
|
case Intrinsic::sadd_with_overflow: // FALLTHROUGH
|
||||||
if (OR == OverflowResult::NeverOverflows)
|
case Intrinsic::usub_with_overflow: // FALLTHROUGH
|
||||||
return CreateOverflowTuple(II, Builder->CreateNUWAdd(LHS, RHS), false);
|
case Intrinsic::ssub_with_overflow: // FALLTHROUGH
|
||||||
if (OR == OverflowResult::AlwaysOverflows)
|
case Intrinsic::umul_with_overflow: // FALLTHROUGH
|
||||||
return CreateOverflowTuple(II, Builder->CreateAdd(LHS, RHS), true);
|
case Intrinsic::smul_with_overflow: {
|
||||||
}
|
|
||||||
// FALL THROUGH uadd into sadd
|
|
||||||
case Intrinsic::sadd_with_overflow:
|
|
||||||
// Canonicalize constants into the RHS.
|
|
||||||
if (isa<Constant>(II->getArgOperand(0)) &&
|
if (isa<Constant>(II->getArgOperand(0)) &&
|
||||||
!isa<Constant>(II->getArgOperand(1))) {
|
!isa<Constant>(II->getArgOperand(1))) {
|
||||||
|
// Canonicalize constants into the RHS.
|
||||||
Value *LHS = II->getArgOperand(0);
|
Value *LHS = II->getArgOperand(0);
|
||||||
II->setArgOperand(0, II->getArgOperand(1));
|
II->setArgOperand(0, II->getArgOperand(1));
|
||||||
II->setArgOperand(1, LHS);
|
II->setArgOperand(1, LHS);
|
||||||
return II;
|
return II;
|
||||||
}
|
}
|
||||||
|
|
||||||
// X + undef -> undef
|
OverflowCheckFlavor OCF =
|
||||||
if (isa<UndefValue>(II->getArgOperand(1)))
|
IntrinsicIDToOverflowCheckFlavor(II->getIntrinsicID());
|
||||||
return ReplaceInstUsesWith(CI, UndefValue::get(II->getType()));
|
assert(OCF != OCF_INVALID && "unexpected!");
|
||||||
|
|
||||||
if (ConstantInt *RHS = dyn_cast<ConstantInt>(II->getArgOperand(1))) {
|
Value *OperationResult = nullptr;
|
||||||
// X + 0 -> {X, false}
|
Constant *OverflowResult = nullptr;
|
||||||
if (RHS->isZero()) {
|
if (OptimizeOverflowCheck(OCF, II->getArgOperand(0), II->getArgOperand(1),
|
||||||
return CreateOverflowTuple(II, II->getArgOperand(0), false,
|
*II, OperationResult, OverflowResult))
|
||||||
/*ReUseName*/false);
|
return CreateOverflowTuple(II, OperationResult, OverflowResult);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can strength reduce reduce this signed add into a regular add if we
|
|
||||||
// can prove that it will never overflow.
|
|
||||||
if (II->getIntrinsicID() == Intrinsic::sadd_with_overflow) {
|
|
||||||
Value *LHS = II->getArgOperand(0), *RHS = II->getArgOperand(1);
|
|
||||||
if (WillNotOverflowSignedAdd(LHS, RHS, *II)) {
|
|
||||||
return CreateOverflowTuple(II, Builder->CreateNSWAdd(LHS, RHS), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Intrinsic::usub_with_overflow:
|
|
||||||
case Intrinsic::ssub_with_overflow: {
|
|
||||||
Value *LHS = II->getArgOperand(0), *RHS = II->getArgOperand(1);
|
|
||||||
// undef - X -> undef
|
|
||||||
// X - undef -> undef
|
|
||||||
if (isa<UndefValue>(LHS) || isa<UndefValue>(RHS))
|
|
||||||
return ReplaceInstUsesWith(CI, UndefValue::get(II->getType()));
|
|
||||||
|
|
||||||
if (ConstantInt *ConstRHS = dyn_cast<ConstantInt>(RHS)) {
|
|
||||||
// X - 0 -> {X, false}
|
|
||||||
if (ConstRHS->isZero()) {
|
|
||||||
return CreateOverflowTuple(II, LHS, false, /*ReUseName*/false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (II->getIntrinsicID() == Intrinsic::ssub_with_overflow) {
|
|
||||||
if (WillNotOverflowSignedSub(LHS, RHS, *II)) {
|
|
||||||
return CreateOverflowTuple(II, Builder->CreateNSWSub(LHS, RHS), false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (WillNotOverflowUnsignedSub(LHS, RHS, *II)) {
|
|
||||||
return CreateOverflowTuple(II, Builder->CreateNUWSub(LHS, RHS), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Intrinsic::umul_with_overflow: {
|
|
||||||
Value *LHS = II->getArgOperand(0), *RHS = II->getArgOperand(1);
|
|
||||||
OverflowResult OR = computeOverflowForUnsignedMul(LHS, RHS, II);
|
|
||||||
if (OR == OverflowResult::NeverOverflows)
|
|
||||||
return CreateOverflowTuple(II, Builder->CreateNUWMul(LHS, RHS), false);
|
|
||||||
if (OR == OverflowResult::AlwaysOverflows)
|
|
||||||
return CreateOverflowTuple(II, Builder->CreateMul(LHS, RHS), true);
|
|
||||||
} // FALL THROUGH
|
|
||||||
case Intrinsic::smul_with_overflow:
|
|
||||||
// Canonicalize constants into the RHS.
|
|
||||||
if (isa<Constant>(II->getArgOperand(0)) &&
|
|
||||||
!isa<Constant>(II->getArgOperand(1))) {
|
|
||||||
Value *LHS = II->getArgOperand(0);
|
|
||||||
II->setArgOperand(0, II->getArgOperand(1));
|
|
||||||
II->setArgOperand(1, LHS);
|
|
||||||
return II;
|
|
||||||
}
|
|
||||||
|
|
||||||
// X * undef -> undef
|
|
||||||
if (isa<UndefValue>(II->getArgOperand(1)))
|
|
||||||
return ReplaceInstUsesWith(CI, UndefValue::get(II->getType()));
|
|
||||||
|
|
||||||
if (ConstantInt *RHSI = dyn_cast<ConstantInt>(II->getArgOperand(1))) {
|
|
||||||
// X*0 -> {0, false}
|
|
||||||
if (RHSI->isZero())
|
|
||||||
return ReplaceInstUsesWith(CI, Constant::getNullValue(II->getType()));
|
|
||||||
|
|
||||||
// X * 1 -> {X, false}
|
|
||||||
if (RHSI->equalsInt(1)) {
|
|
||||||
return CreateOverflowTuple(II, II->getArgOperand(0), false,
|
|
||||||
/*ReUseName*/false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (II->getIntrinsicID() == Intrinsic::smul_with_overflow) {
|
|
||||||
Value *LHS = II->getArgOperand(0), *RHS = II->getArgOperand(1);
|
|
||||||
if (WillNotOverflowSignedMul(LHS, RHS, *II)) {
|
|
||||||
return CreateOverflowTuple(II, Builder->CreateNSWMul(LHS, RHS), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Intrinsic::minnum:
|
case Intrinsic::minnum:
|
||||||
case Intrinsic::maxnum: {
|
case Intrinsic::maxnum: {
|
||||||
Value *Arg0 = II->getArgOperand(0);
|
Value *Arg0 = II->getArgOperand(0);
|
||||||
|
|
|
@ -2138,6 +2138,113 @@ static Instruction *ProcessUAddIdiom(Instruction &I, Value *OrigAddV,
|
||||||
return ExtractValueInst::Create(Call, 1, "uadd.overflow");
|
return ExtractValueInst::Create(Call, 1, "uadd.overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InstCombiner::OptimizeOverflowCheck(OverflowCheckFlavor OCF, Value *LHS,
|
||||||
|
Value *RHS, Instruction &OrigI,
|
||||||
|
Value *&Result, Constant *&Overflow) {
|
||||||
|
assert(!(isa<Constant>(LHS) && !isa<Constant>(RHS)) &&
|
||||||
|
"call with a constant RHS if possible!");
|
||||||
|
|
||||||
|
auto SetResult = [&](Value *OpResult, Constant *OverflowVal, bool ReuseName) {
|
||||||
|
Result = OpResult;
|
||||||
|
Overflow = OverflowVal;
|
||||||
|
if (ReuseName)
|
||||||
|
Result->takeName(&OrigI);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (OCF) {
|
||||||
|
case OCF_INVALID:
|
||||||
|
llvm_unreachable("bad overflow check kind!");
|
||||||
|
|
||||||
|
case OCF_UNSIGNED_ADD: {
|
||||||
|
OverflowResult OR = computeOverflowForUnsignedAdd(LHS, RHS, &OrigI);
|
||||||
|
if (OR == OverflowResult::NeverOverflows)
|
||||||
|
return SetResult(Builder->CreateNUWAdd(LHS, RHS), Builder->getFalse(),
|
||||||
|
true);
|
||||||
|
|
||||||
|
if (OR == OverflowResult::AlwaysOverflows)
|
||||||
|
return SetResult(Builder->CreateAdd(LHS, RHS), Builder->getTrue(), true);
|
||||||
|
}
|
||||||
|
// FALL THROUGH uadd into sadd
|
||||||
|
case OCF_SIGNED_ADD: {
|
||||||
|
// X + undef -> undef
|
||||||
|
if (isa<UndefValue>(RHS))
|
||||||
|
return SetResult(UndefValue::get(RHS->getType()),
|
||||||
|
UndefValue::get(Builder->getInt1Ty()), false);
|
||||||
|
|
||||||
|
if (ConstantInt *ConstRHS = dyn_cast<ConstantInt>(RHS))
|
||||||
|
// X + 0 -> {X, false}
|
||||||
|
if (ConstRHS->isZero())
|
||||||
|
return SetResult(LHS, Builder->getFalse(), false);
|
||||||
|
|
||||||
|
// We can strength reduce this signed add into a regular add if we can prove
|
||||||
|
// that it will never overflow.
|
||||||
|
if (OCF == OCF_SIGNED_ADD)
|
||||||
|
if (WillNotOverflowSignedAdd(LHS, RHS, OrigI))
|
||||||
|
return SetResult(Builder->CreateNSWAdd(LHS, RHS), Builder->getFalse(),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
case OCF_UNSIGNED_SUB:
|
||||||
|
case OCF_SIGNED_SUB: {
|
||||||
|
// undef - X -> undef
|
||||||
|
// X - undef -> undef
|
||||||
|
if (isa<UndefValue>(LHS) || isa<UndefValue>(RHS))
|
||||||
|
return SetResult(UndefValue::get(LHS->getType()),
|
||||||
|
UndefValue::get(Builder->getInt1Ty()), false);
|
||||||
|
|
||||||
|
if (ConstantInt *ConstRHS = dyn_cast<ConstantInt>(RHS))
|
||||||
|
// X - 0 -> {X, false}
|
||||||
|
if (ConstRHS->isZero())
|
||||||
|
return SetResult(UndefValue::get(LHS->getType()), Builder->getFalse(),
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (OCF == OCF_SIGNED_SUB) {
|
||||||
|
if (WillNotOverflowSignedSub(LHS, RHS, OrigI))
|
||||||
|
return SetResult(Builder->CreateNSWSub(LHS, RHS), Builder->getFalse(),
|
||||||
|
true);
|
||||||
|
} else {
|
||||||
|
if (WillNotOverflowUnsignedSub(LHS, RHS, OrigI))
|
||||||
|
return SetResult(Builder->CreateNUWSub(LHS, RHS), Builder->getFalse(),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OCF_UNSIGNED_MUL: {
|
||||||
|
OverflowResult OR = computeOverflowForUnsignedMul(LHS, RHS, &OrigI);
|
||||||
|
if (OR == OverflowResult::NeverOverflows)
|
||||||
|
return SetResult(Builder->CreateNUWMul(LHS, RHS), Builder->getFalse(),
|
||||||
|
true);
|
||||||
|
if (OR == OverflowResult::AlwaysOverflows)
|
||||||
|
return SetResult(Builder->CreateMul(LHS, RHS), Builder->getTrue(), true);
|
||||||
|
} // FALL THROUGH
|
||||||
|
case OCF_SIGNED_MUL:
|
||||||
|
// X * undef -> undef
|
||||||
|
if (isa<UndefValue>(RHS))
|
||||||
|
return SetResult(UndefValue::get(LHS->getType()),
|
||||||
|
UndefValue::get(Builder->getInt1Ty()), false);
|
||||||
|
|
||||||
|
if (ConstantInt *RHSI = dyn_cast<ConstantInt>(RHS)) {
|
||||||
|
// X * 0 -> {0, false}
|
||||||
|
if (RHSI->isZero())
|
||||||
|
return SetResult(Constant::getNullValue(RHS->getType()),
|
||||||
|
Builder->getFalse(), false);
|
||||||
|
|
||||||
|
// X * 1 -> {X, false}
|
||||||
|
if (RHSI->equalsInt(1))
|
||||||
|
return SetResult(LHS, Builder->getFalse(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OCF == OCF_SIGNED_MUL)
|
||||||
|
if (WillNotOverflowSignedMul(LHS, RHS, OrigI))
|
||||||
|
return SetResult(Builder->CreateNSWMul(LHS, RHS), Builder->getFalse(),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Recognize and process idiom involving test for multiplication
|
/// \brief Recognize and process idiom involving test for multiplication
|
||||||
/// overflow.
|
/// overflow.
|
||||||
///
|
///
|
||||||
|
|
|
@ -110,6 +110,41 @@ static inline bool IsFreeToInvert(Value *V, bool WillInvertAllUses) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Specific patterns of overflow check idioms that we match.
|
||||||
|
enum OverflowCheckFlavor {
|
||||||
|
OCF_UNSIGNED_ADD,
|
||||||
|
OCF_SIGNED_ADD,
|
||||||
|
OCF_UNSIGNED_SUB,
|
||||||
|
OCF_SIGNED_SUB,
|
||||||
|
OCF_UNSIGNED_MUL,
|
||||||
|
OCF_SIGNED_MUL,
|
||||||
|
|
||||||
|
OCF_INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Returns the OverflowCheckFlavor corresponding to a overflow_with_op
|
||||||
|
/// intrinsic.
|
||||||
|
static inline OverflowCheckFlavor
|
||||||
|
IntrinsicIDToOverflowCheckFlavor(unsigned ID) {
|
||||||
|
switch (ID) {
|
||||||
|
default:
|
||||||
|
return OCF_INVALID;
|
||||||
|
case Intrinsic::uadd_with_overflow:
|
||||||
|
return OCF_UNSIGNED_ADD;
|
||||||
|
case Intrinsic::sadd_with_overflow:
|
||||||
|
return OCF_SIGNED_ADD;
|
||||||
|
case Intrinsic::usub_with_overflow:
|
||||||
|
return OCF_UNSIGNED_SUB;
|
||||||
|
case Intrinsic::ssub_with_overflow:
|
||||||
|
return OCF_SIGNED_SUB;
|
||||||
|
case Intrinsic::umul_with_overflow:
|
||||||
|
return OCF_UNSIGNED_MUL;
|
||||||
|
case Intrinsic::smul_with_overflow:
|
||||||
|
return OCF_SIGNED_MUL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief An IRBuilder inserter that adds new instructions to the instcombine
|
/// \brief An IRBuilder inserter that adds new instructions to the instcombine
|
||||||
/// worklist.
|
/// worklist.
|
||||||
class LLVM_LIBRARY_VISIBILITY InstCombineIRInserter
|
class LLVM_LIBRARY_VISIBILITY InstCombineIRInserter
|
||||||
|
@ -329,6 +364,17 @@ private:
|
||||||
bool ShouldOptimizeCast(Instruction::CastOps opcode, const Value *V,
|
bool ShouldOptimizeCast(Instruction::CastOps opcode, const Value *V,
|
||||||
Type *Ty);
|
Type *Ty);
|
||||||
|
|
||||||
|
/// \brief Try to optimize a sequence of instructions checking if an operation
|
||||||
|
/// on LHS and RHS overflows.
|
||||||
|
///
|
||||||
|
/// If a simplification is possible, stores the simplified result of the
|
||||||
|
/// operation in OperationResult and result of the overflow check in
|
||||||
|
/// OverflowResult, and return true. If no simplification is possible,
|
||||||
|
/// returns false.
|
||||||
|
bool OptimizeOverflowCheck(OverflowCheckFlavor OCF, Value *LHS, Value *RHS,
|
||||||
|
Instruction &CtxI, Value *&OperationResult,
|
||||||
|
Constant *&OverflowResult);
|
||||||
|
|
||||||
Instruction *visitCallSite(CallSite CS);
|
Instruction *visitCallSite(CallSite CS);
|
||||||
Instruction *tryOptimizeCall(CallInst *CI);
|
Instruction *tryOptimizeCall(CallInst *CI);
|
||||||
bool transformConstExprCastCall(CallSite CS);
|
bool transformConstExprCastCall(CallSite CS);
|
||||||
|
@ -391,14 +437,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a result tuple for an overflow intrinsic \p II with a given
|
/// Creates a result tuple for an overflow intrinsic \p II with a given
|
||||||
/// \p Result and a constant \p Overflow value. If \p ReUseName is true the
|
/// \p Result and a constant \p Overflow value.
|
||||||
/// \p Result's name is taken from \p II.
|
|
||||||
Instruction *CreateOverflowTuple(IntrinsicInst *II, Value *Result,
|
Instruction *CreateOverflowTuple(IntrinsicInst *II, Value *Result,
|
||||||
bool Overflow, bool ReUseName = true) {
|
Constant *Overflow) {
|
||||||
if (ReUseName)
|
Constant *V[] = {UndefValue::get(Result->getType()), Overflow};
|
||||||
Result->takeName(II);
|
|
||||||
Constant *V[] = {UndefValue::get(Result->getType()),
|
|
||||||
Overflow ? Builder->getTrue() : Builder->getFalse()};
|
|
||||||
StructType *ST = cast<StructType>(II->getType());
|
StructType *ST = cast<StructType>(II->getType());
|
||||||
Constant *Struct = ConstantStruct::get(ST, V);
|
Constant *Struct = ConstantStruct::get(ST, V);
|
||||||
return InsertValueInst::Create(Struct, Result, 0);
|
return InsertValueInst::Create(Struct, Result, 0);
|
||||||
|
|
Loading…
Reference in New Issue