forked from OSchip/llvm-project
[IndVars] Have `cloneArithmeticIVUser` guess better
Summary: `cloneArithmeticIVUser` currently trips over expression like `add %iv, -1` when `%iv` is being zero extended -- it tries to construct the widened use as `add %iv.zext, zext(-1)` and (correctly) fails to prove equivalence to `zext(add %iv, -1)` (here the SCEV for `%iv` is `{1,+,1}`). This change teaches `IndVars` to try sign extending the non-IV operand if that makes the newly constructed IV use equivalent to the widened narrow IV use. Reviewers: atrick, hfinkel, reames Subscribers: sanjoy, llvm-commits Differential Revision: http://reviews.llvm.org/D13717 llvm-svn: 250483
This commit is contained in:
parent
472840a3d3
commit
37e87c2023
|
@ -998,22 +998,84 @@ Instruction *WidenIV::cloneArithmeticIVUser(NarrowIVDefUse DU,
|
|||
|
||||
DEBUG(dbgs() << "Cloning arithmetic IVUser: " << *NarrowUse << "\n");
|
||||
|
||||
// Replace NarrowDef operands with WideDef. Otherwise, we don't know anything
|
||||
// about the narrow operand yet so must insert a [sz]ext. It is probably loop
|
||||
// invariant and will be folded or hoisted. If it actually comes from a
|
||||
// widened IV, it should be removed during a future call to widenIVUse.
|
||||
Value *LHS =
|
||||
(NarrowUse->getOperand(0) == NarrowDef)
|
||||
? WideDef
|
||||
: getExtend(NarrowUse->getOperand(0), WideType, IsSigned, NarrowUse);
|
||||
Value *RHS =
|
||||
(NarrowUse->getOperand(1) == NarrowDef)
|
||||
? WideDef
|
||||
: getExtend(NarrowUse->getOperand(1), WideType, IsSigned, NarrowUse);
|
||||
unsigned IVOpIdx = (NarrowUse->getOperand(0) == NarrowDef) ? 0 : 1;
|
||||
|
||||
// We're trying to find X such that
|
||||
//
|
||||
// Widen(NarrowDef `op` NonIVNarrowDef) == WideAR == WideDef `op.wide` X
|
||||
//
|
||||
// We guess two solutions to X, sext(NonIVNarrowDef) and zext(NonIVNarrowDef),
|
||||
// and check using SCEV if any of them are correct.
|
||||
|
||||
// Returns true if extending NonIVNarrowDef according to `SignExt` is a
|
||||
// correct solution to X.
|
||||
auto GuessNonIVOperand = [&](bool SignExt) {
|
||||
const SCEV *WideLHS;
|
||||
const SCEV *WideRHS;
|
||||
|
||||
auto GetExtend = [this, SignExt](const SCEV *S, Type *Ty) {
|
||||
if (SignExt)
|
||||
return SE->getSignExtendExpr(S, Ty);
|
||||
return SE->getZeroExtendExpr(S, Ty);
|
||||
};
|
||||
|
||||
if (IVOpIdx == 0) {
|
||||
WideLHS = SE->getSCEV(WideDef);
|
||||
const SCEV *NarrowRHS = SE->getSCEV(NarrowUse->getOperand(1));
|
||||
WideRHS = GetExtend(NarrowRHS, WideType);
|
||||
} else {
|
||||
const SCEV *NarrowLHS = SE->getSCEV(NarrowUse->getOperand(0));
|
||||
WideLHS = GetExtend(NarrowLHS, WideType);
|
||||
WideRHS = SE->getSCEV(WideDef);
|
||||
}
|
||||
|
||||
// WideUse is "WideDef `op.wide` X" as described in the comment.
|
||||
const SCEV *WideUse = nullptr;
|
||||
|
||||
switch (NarrowUse->getOpcode()) {
|
||||
default:
|
||||
llvm_unreachable("No other possibility!");
|
||||
|
||||
case Instruction::Add:
|
||||
WideUse = SE->getAddExpr(WideLHS, WideRHS);
|
||||
break;
|
||||
|
||||
case Instruction::Mul:
|
||||
WideUse = SE->getMulExpr(WideLHS, WideRHS);
|
||||
break;
|
||||
|
||||
case Instruction::UDiv:
|
||||
WideUse = SE->getUDivExpr(WideLHS, WideRHS);
|
||||
break;
|
||||
|
||||
case Instruction::Sub:
|
||||
WideUse = SE->getMinusSCEV(WideLHS, WideRHS);
|
||||
break;
|
||||
}
|
||||
|
||||
return WideUse == WideAR;
|
||||
};
|
||||
|
||||
bool SignExtend = IsSigned;
|
||||
if (!GuessNonIVOperand(SignExtend)) {
|
||||
SignExtend = !SignExtend;
|
||||
if (!GuessNonIVOperand(SignExtend))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value *LHS = (NarrowUse->getOperand(0) == NarrowDef)
|
||||
? WideDef
|
||||
: getExtend(NarrowUse->getOperand(0), WideType, SignExtend,
|
||||
NarrowUse);
|
||||
Value *RHS = (NarrowUse->getOperand(1) == NarrowDef)
|
||||
? WideDef
|
||||
: getExtend(NarrowUse->getOperand(1), WideType, SignExtend,
|
||||
NarrowUse);
|
||||
|
||||
auto *NarrowBO = cast<BinaryOperator>(NarrowUse);
|
||||
auto *WideBO = BinaryOperator::Create(NarrowBO->getOpcode(), LHS, RHS,
|
||||
NarrowBO->getName());
|
||||
|
||||
IRBuilder<> Builder(NarrowUse);
|
||||
Builder.Insert(WideBO);
|
||||
if (const auto *OBO = dyn_cast<OverflowingBinaryOperator>(NarrowBO)) {
|
||||
|
|
|
@ -6,7 +6,7 @@ target datalayout = "n8:16:32:64"
|
|||
|
||||
target triple = "x86_64-apple-darwin"
|
||||
|
||||
; CHECK-LABEL: @sloop
|
||||
; CHECK-LABEL: @loop_0
|
||||
; CHECK-LABEL: B18:
|
||||
; Only one phi now.
|
||||
; CHECK: phi
|
||||
|
@ -16,7 +16,7 @@ target triple = "x86_64-apple-darwin"
|
|||
; One trunc for the dummy() call.
|
||||
; CHECK-LABEL: exit24:
|
||||
; CHECK: trunc i64 {{.*}}lcssa.wide to i32
|
||||
define void @sloop(i32* %a) {
|
||||
define void @loop_0(i32* %a) {
|
||||
Prologue:
|
||||
br i1 undef, label %B18, label %B6
|
||||
|
||||
|
@ -41,4 +41,30 @@ exit24: ; preds = %B18
|
|||
unreachable
|
||||
}
|
||||
|
||||
define void @loop_1(i32 %lim) {
|
||||
; CHECK-LABEL: @loop_1(
|
||||
entry:
|
||||
%entry.cond = icmp ne i32 %lim, 0
|
||||
br i1 %entry.cond, label %loop, label %leave
|
||||
|
||||
loop:
|
||||
; CHECK: loop:
|
||||
; CHECK: %indvars.iv = phi i64 [ 1, %loop.preheader ], [ %indvars.iv.next, %loop ]
|
||||
; CHECK: %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
|
||||
; CHECK: [[IV_INC:%[^ ]+]] = add nsw i64 %indvars.iv, -1
|
||||
; CHECK: call void @dummy.i64(i64 [[IV_INC]])
|
||||
|
||||
%iv = phi i32 [ 1, %entry ], [ %iv.inc, %loop ]
|
||||
%iv.inc = add i32 %iv, 1
|
||||
%iv.inc.sub = add i32 %iv, -1
|
||||
%iv.inc.sub.zext = zext i32 %iv.inc.sub to i64
|
||||
call void @dummy.i64(i64 %iv.inc.sub.zext)
|
||||
%be.cond = icmp ult i32 %iv.inc, %lim
|
||||
br i1 %be.cond, label %loop, label %leave
|
||||
|
||||
leave:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @dummy(i32)
|
||||
declare void @dummy.i64(i64)
|
||||
|
|
Loading…
Reference in New Issue