Index: lib/Transforms/Utils/SimplifyIndVar.cpp =================================================================== --- lib/Transforms/Utils/SimplifyIndVar.cpp +++ lib/Transforms/Utils/SimplifyIndVar.cpp @@ -521,9 +521,36 @@ } /// Annotate BO with nsw / nuw if it provably does not signed-overflow / -/// unsigned-overflow. Returns true if anything changed, false otherwise. +/// unsigned-overflow. Also check for shifts that can be marked as exact. +/// Returns true if anything changed, false otherwise. bool SimplifyIndvar::strengthenOverflowingOperation(BinaryOperator *BO, Value *IVOperand) { + // match (X << IVOperand) >> C, marking the AShr as exact using the + // information from the IV's range + if(BO->getOpcode() == Instruction::Shl) { + bool Changed = false; + for(auto* user : BO->users()) { + BinaryOperator* AShr = dyn_cast(user); + if(!AShr || AShr->getOpcode() != Instruction::AShr || + AShr->getOperand(0) != BO || AShr->isExact()) + continue; + + ConstantRange IVRange = SE->getUnsignedRange(SE->getSCEV(IVOperand)); + + Value* ShlOp1 = BO->getOperand(0); + if(ShlOp1 == IVOperand || !BO->getType()->isIntegerTy() || + IVRange.getUpper().uge(BO->getType()->getPrimitiveSizeInBits())) + continue; + + ConstantInt* AShrOp2 = dyn_cast(AShr->getOperand(1)); + if(!AShrOp2 || IVRange.getLower().ult(AShrOp2->getValue())) + continue; + + AShr->setIsExact(true); + Changed = true; + } + return Changed; + } // Fastpath: we don't have any work to do if `BO` is `nuw` and `nsw`. if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap()) Index: test/Transforms/IndVarSimplify/strengthen-overflow.ll =================================================================== --- test/Transforms/IndVarSimplify/strengthen-overflow.ll +++ test/Transforms/IndVarSimplify/strengthen-overflow.ll @@ -104,5 +104,37 @@ ret i32 42 } +define hidden void @test.shl() { +; CHECK-LABEL: @test.shl +entry: + br label %for.body + +for.body: +; CHECK-LABEL: for.body + %k.021 = phi i32 [ 1, %entry ], [ %inc, %for.body ] + %shl = shl i32 1, %k.021 + %shr = ashr i32 %shl, 1 +; CHECK: %shr = ashr exact i32 %shl, 1 + %div = sdiv i32 %shr, 2 + %add = add nsw i32 %div, %shr + %arrayidx = getelementptr inbounds [16 x i32], [16 x i32]* @Data, i32 0, i32 %add + %arrayidx1 = getelementptr inbounds [16 x i32], [16 x i32]* @Data, i32 0, i32 %div + %0 = load i32, i32* %arrayidx, align 4 + %1 = load i32, i32* %arrayidx1, align 4 + %sub = sub nsw i32 %1, %0 + store i32 %sub, i32* %arrayidx, align 4 + %2 = load i32, i32* %arrayidx1, align 4 + %add4 = add nsw i32 %2, %0 + store i32 %add4, i32* %arrayidx1, align 4 + %inc = add nuw nsw i32 %k.021, 1 + %exitcond = icmp eq i32 %inc, 9 + br i1 %exitcond, label %for.end, label %for.body + +for.end: + ret void +} + +@Data = hidden local_unnamed_addr global [16 x i32] zeroinitializer, align 4 + !0 = !{i32 0, i32 2} !1 = !{i32 0, i32 42}