if (!Op)
return None;
+ // Recognise intrinsic loop.decrement.reg, and as this has exactly the same
+ // semantics as a Sub, return a binary sub expression.
+ if (auto *II = dyn_cast<IntrinsicInst>(V)) {
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::loop_decrement_reg:
+ return BinaryOp(Instruction::Sub, II->getOperand(0), II->getOperand(1));
+ default:
+ return None;
+ }
+ }
+
// Implementation detail: all the cleverness here should happen without
// creating new SCEV expressions -- our caller knowns tricks to avoid creating
// SCEV expressions when possible, and we should not break that.
ret i32 %i
}
+define i32 @quadratic_sgt_loopdec() {
+; CHECK-LABEL: @quadratic_sgt_loopdec(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[I:%.*]] = phi i32 [ 10, [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[I_NEXT]] = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 [[I]], i32 1)
+; CHECK-NEXT: store i32 [[I]], i32* @A
+; CHECK-NEXT: [[I2:%.*]] = mul i32 [[I]], [[I]]
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp sgt i32 [[I2]], 0
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LOOPEXIT:%.*]]
+; CHECK: loopexit:
+; CHECK-NEXT: ret i32 0
+
+entry:
+ br label %loop
+
+loop:
+ %i = phi i32 [ 10, %entry ], [ %i.next, %loop ]
+ %i.next = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 %i, i32 1)
+ store i32 %i, i32* @A
+ %i2 = mul i32 %i, %i
+ %c = icmp sgt i32 %i2, 0
+ br i1 %c, label %loop, label %loopexit
+
+loopexit:
+ ret i32 %i
+}
@data = common global [240 x i8] zeroinitializer, align 16
}
+declare i32 @llvm.loop.decrement.reg.i32.i32.i32(i32, i32)
--- /dev/null
+; RUN: opt -mtriple=thumbv8.1m.main -mattr=+mve.fp -loop-unroll --loop-unroll -S < %s | FileCheck %s
+
+; CHECK-LABEL: foo
+; CHECK: 5:
+; CHECK: 6: ; preds = %6, %5
+; CHECK: 15: ; preds = %6
+; CHECK: br label %16
+; CHECK: 16: ; preds = %15, %3
+; CHECK: ret void
+; CHECK: }
+
+define void @foo(i8* nocapture, i8* nocapture readonly, i32) {
+ %4 = icmp sgt i32 %2, 0
+ br i1 %4, label %5, label %16
+
+; <label>:5:
+ br label %6
+
+; <label>:6:
+ %7 = phi i32 [ %13, %6 ], [ %2, %5 ]
+ %8 = phi i8* [ %10, %6 ], [ %1, %5 ]
+ %9 = phi i8* [ %12, %6 ], [ %0, %5 ]
+ %10 = getelementptr inbounds i8, i8* %8, i32 1
+ %11 = load i8, i8* %8, align 1
+ %12 = getelementptr inbounds i8, i8* %9, i32 1
+ store i8 %11, i8* %9, align 1
+
+ %13 = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 %7, i32 1)
+
+ %14 = icmp sgt i32 %7, 1
+ br i1 %14, label %6, label %15
+
+; <label>:15:
+ br label %16
+
+; <label>:16:
+ ret void
+}
+
+declare i32 @llvm.loop.decrement.reg.i32.i32.i32(i32, i32)
"} ");
}
+TEST_F(ScalarEvolutionsTest, SCEVLoopDecIntrinsic) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "define void @foo(i32 %N) { "
+ "entry: "
+ " %cmp3 = icmp sgt i32 %N, 0 "
+ " br i1 %cmp3, label %for.body, label %for.cond.cleanup "
+ "for.cond.cleanup: "
+ " ret void "
+ "for.body: "
+ " %i.04 = phi i32 [ %inc, %for.body ], [ 100, %entry ] "
+ " %inc = call i32 @llvm.loop.decrement.reg.i32.i32.i32(i32 %i.04, i32 1) "
+ " %exitcond = icmp ne i32 %inc, 0 "
+ " br i1 %exitcond, label %for.cond.cleanup, label %for.body "
+ "} "
+ "declare i32 @llvm.loop.decrement.reg.i32.i32.i32(i32, i32) ",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ runWithSE(*M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ auto *ScevInc = SE.getSCEV(getInstructionByName(F, "inc"));
+ EXPECT_TRUE(isa<SCEVAddRecExpr>(ScevInc));
+ });
+}
+
TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) {
LLVMContext C;
SMDiagnostic Err;