[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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@234388 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Sanjoy Das 2015-04-08 04:27:22 +00:00
parent b343d1cd85
commit 368f045de1
3 changed files with 172 additions and 100 deletions

View File

@ -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);

View File

@ -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.
/// ///

View File

@ -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);