137b7922dSKit Barton //===- IVDescriptorsTest.cpp - IVDescriptors unit tests -------------------===// 237b7922dSKit Barton // 337b7922dSKit Barton // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 437b7922dSKit Barton // See https://llvm.org/LICENSE.txt for license information. 537b7922dSKit Barton // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 637b7922dSKit Barton // 737b7922dSKit Barton //===----------------------------------------------------------------------===// 837b7922dSKit Barton 937b7922dSKit Barton #include "llvm/Analysis/IVDescriptors.h" 1037b7922dSKit Barton #include "llvm/Analysis/AssumptionCache.h" 1137b7922dSKit Barton #include "llvm/Analysis/LoopInfo.h" 1237b7922dSKit Barton #include "llvm/Analysis/ScalarEvolution.h" 1337b7922dSKit Barton #include "llvm/Analysis/TargetLibraryInfo.h" 1437b7922dSKit Barton #include "llvm/AsmParser/Parser.h" 1537b7922dSKit Barton #include "llvm/IR/Dominators.h" 16*4169338eSNikita Popov #include "llvm/IR/Module.h" 1737b7922dSKit Barton #include "llvm/Support/SourceMgr.h" 1837b7922dSKit Barton #include "gtest/gtest.h" 1937b7922dSKit Barton 2037b7922dSKit Barton using namespace llvm; 2137b7922dSKit Barton 2237b7922dSKit Barton /// Build the loop info and scalar evolution for the function and run the Test. 2337b7922dSKit Barton static void runWithLoopInfoAndSE( 2437b7922dSKit Barton Module &M, StringRef FuncName, 2537b7922dSKit Barton function_ref<void(Function &F, LoopInfo &LI, ScalarEvolution &SE)> Test) { 2637b7922dSKit Barton auto *F = M.getFunction(FuncName); 2737b7922dSKit Barton ASSERT_NE(F, nullptr) << "Could not find " << FuncName; 2837b7922dSKit Barton 2937b7922dSKit Barton TargetLibraryInfoImpl TLII; 3037b7922dSKit Barton TargetLibraryInfo TLI(TLII); 3137b7922dSKit Barton AssumptionCache AC(*F); 3237b7922dSKit Barton DominatorTree DT(*F); 3337b7922dSKit Barton LoopInfo LI(DT); 3437b7922dSKit Barton ScalarEvolution SE(*F, TLI, AC, DT, LI); 3537b7922dSKit Barton 3637b7922dSKit Barton Test(*F, LI, SE); 3737b7922dSKit Barton } 3837b7922dSKit Barton 3937b7922dSKit Barton static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { 4037b7922dSKit Barton SMDiagnostic Err; 4137b7922dSKit Barton std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C); 4237b7922dSKit Barton if (!Mod) 4337b7922dSKit Barton Err.print("IVDescriptorsTests", errs()); 4437b7922dSKit Barton return Mod; 4537b7922dSKit Barton } 4637b7922dSKit Barton 4737b7922dSKit Barton // This tests that IVDescriptors can obtain the induction binary operator for 48912b3b03SJim Lin // integer induction variables. And getExactFPMathInst() correctly return the 49912b3b03SJim Lin // expected behavior, i.e. no FMF algebra. 5037b7922dSKit Barton TEST(IVDescriptorsTest, LoopWithSingleLatch) { 5137b7922dSKit Barton // Parse the module. 5237b7922dSKit Barton LLVMContext Context; 5337b7922dSKit Barton 5437b7922dSKit Barton std::unique_ptr<Module> M = parseIR( 5537b7922dSKit Barton Context, 5639197691SNikita Popov R"(define void @foo(ptr %A, i32 %ub) { 5737b7922dSKit Barton entry: 5837b7922dSKit Barton br label %for.body 5937b7922dSKit Barton for.body: 6037b7922dSKit Barton %i = phi i32 [ 0, %entry ], [ %inc, %for.body ] 6137b7922dSKit Barton %idxprom = sext i32 %i to i64 6239197691SNikita Popov %arrayidx = getelementptr inbounds i32, ptr %A, i64 %idxprom 6339197691SNikita Popov store i32 %i, ptr %arrayidx, align 4 6437b7922dSKit Barton %inc = add nsw i32 %i, 1 6537b7922dSKit Barton %cmp = icmp slt i32 %inc, %ub 6637b7922dSKit Barton br i1 %cmp, label %for.body, label %for.exit 6737b7922dSKit Barton for.exit: 6837b7922dSKit Barton br label %for.end 6937b7922dSKit Barton for.end: 7037b7922dSKit Barton ret void 7137b7922dSKit Barton })" 7237b7922dSKit Barton ); 7337b7922dSKit Barton 7437b7922dSKit Barton runWithLoopInfoAndSE( 7537b7922dSKit Barton *M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { 7637b7922dSKit Barton Function::iterator FI = F.begin(); 7737b7922dSKit Barton // First basic block is entry - skip it. 7837b7922dSKit Barton BasicBlock *Header = &*(++FI); 7937b7922dSKit Barton assert(Header->getName() == "for.body"); 8037b7922dSKit Barton Loop *L = LI.getLoopFor(Header); 8137b7922dSKit Barton EXPECT_NE(L, nullptr); 8237b7922dSKit Barton PHINode *Inst_i = dyn_cast<PHINode>(&Header->front()); 8337b7922dSKit Barton assert(Inst_i->getName() == "i"); 8437b7922dSKit Barton InductionDescriptor IndDesc; 8537b7922dSKit Barton bool IsInductionPHI = 8637b7922dSKit Barton InductionDescriptor::isInductionPHI(Inst_i, L, &SE, IndDesc); 8737b7922dSKit Barton EXPECT_TRUE(IsInductionPHI); 8837b7922dSKit Barton Instruction *Inst_inc = nullptr; 8937b7922dSKit Barton BasicBlock::iterator BBI = Header->begin(); 9037b7922dSKit Barton do { 9137b7922dSKit Barton if ((&*BBI)->getName() == "inc") 9237b7922dSKit Barton Inst_inc = &*BBI; 9337b7922dSKit Barton ++BBI; 9437b7922dSKit Barton } while (!Inst_inc); 9537b7922dSKit Barton assert(Inst_inc->getName() == "inc"); 9637b7922dSKit Barton EXPECT_EQ(IndDesc.getInductionBinOp(), Inst_inc); 9736a489d1SSanjay Patel EXPECT_EQ(IndDesc.getExactFPMathInst(), nullptr); 9837b7922dSKit Barton }); 9937b7922dSKit Barton } 1007ee6c402SRoman Lebedev 1017ee6c402SRoman Lebedev // Depending on how SCEV deals with ptrtoint cast, the step of a phi could be 1027ee6c402SRoman Lebedev // a pointer, and InductionDescriptor used to fail with an assertion. 1037ee6c402SRoman Lebedev // So just check that it doesn't assert. 1047ee6c402SRoman Lebedev TEST(IVDescriptorsTest, LoopWithPtrToInt) { 1057ee6c402SRoman Lebedev // Parse the module. 1067ee6c402SRoman Lebedev LLVMContext Context; 1077ee6c402SRoman Lebedev 1087ee6c402SRoman Lebedev std::unique_ptr<Module> M = parseIR(Context, R"( 1097ee6c402SRoman Lebedev target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" 1107ee6c402SRoman Lebedev target triple = "thumbv6m-arm-none-eabi" 1117ee6c402SRoman Lebedev 1127ee6c402SRoman Lebedev declare void @widget() 1137ee6c402SRoman Lebedev declare void @wobble(i32) 1147ee6c402SRoman Lebedev 11539197691SNikita Popov define void @barney(ptr %arg, ptr %arg18, i32 %arg19) { 1167ee6c402SRoman Lebedev bb: 11739197691SNikita Popov %tmp = ptrtoint ptr %arg to i32 11839197691SNikita Popov %tmp20 = ptrtoint ptr %arg18 to i32 1197ee6c402SRoman Lebedev %tmp21 = or i32 %tmp20, %tmp 1207ee6c402SRoman Lebedev %tmp22 = and i32 %tmp21, 3 1217ee6c402SRoman Lebedev %tmp23 = icmp eq i32 %tmp22, 0 1227ee6c402SRoman Lebedev br i1 %tmp23, label %bb24, label %bb25 1237ee6c402SRoman Lebedev 1247ee6c402SRoman Lebedev bb24: 1257ee6c402SRoman Lebedev tail call void @widget() 1267ee6c402SRoman Lebedev br label %bb34 1277ee6c402SRoman Lebedev 1287ee6c402SRoman Lebedev bb25: 1297ee6c402SRoman Lebedev %tmp26 = sub i32 %tmp, %tmp20 1307ee6c402SRoman Lebedev %tmp27 = icmp ult i32 %tmp26, %arg19 1317ee6c402SRoman Lebedev br i1 %tmp27, label %bb28, label %bb34 1327ee6c402SRoman Lebedev 1337ee6c402SRoman Lebedev bb28: 1347ee6c402SRoman Lebedev br label %bb29 1357ee6c402SRoman Lebedev 1367ee6c402SRoman Lebedev bb29: 1377ee6c402SRoman Lebedev %tmp30 = phi i32 [ %tmp31, %bb29 ], [ %arg19, %bb28 ] 1387ee6c402SRoman Lebedev tail call void @wobble(i32 %tmp26) 1397ee6c402SRoman Lebedev %tmp31 = sub i32 %tmp30, %tmp26 1407ee6c402SRoman Lebedev %tmp32 = icmp ugt i32 %tmp31, %tmp26 1417ee6c402SRoman Lebedev br i1 %tmp32, label %bb29, label %bb33 1427ee6c402SRoman Lebedev 1437ee6c402SRoman Lebedev bb33: 1447ee6c402SRoman Lebedev br label %bb34 1457ee6c402SRoman Lebedev 1467ee6c402SRoman Lebedev bb34: 1477ee6c402SRoman Lebedev ret void 1487ee6c402SRoman Lebedev })"); 1497ee6c402SRoman Lebedev 1507ee6c402SRoman Lebedev runWithLoopInfoAndSE( 1517ee6c402SRoman Lebedev *M, "barney", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { 1527ee6c402SRoman Lebedev Function::iterator FI = F.begin(); 1537ee6c402SRoman Lebedev // First basic block is entry - skip it. 1547ee6c402SRoman Lebedev BasicBlock *Header = &*(++(++(++(++FI)))); 1557ee6c402SRoman Lebedev assert(Header->getName() == "bb29"); 1567ee6c402SRoman Lebedev Loop *L = LI.getLoopFor(Header); 1577ee6c402SRoman Lebedev EXPECT_NE(L, nullptr); 1587ee6c402SRoman Lebedev PHINode *Inst_i = dyn_cast<PHINode>(&Header->front()); 1597ee6c402SRoman Lebedev assert(Inst_i->getName() == "tmp30"); 1607ee6c402SRoman Lebedev InductionDescriptor IndDesc; 1617ee6c402SRoman Lebedev bool IsInductionPHI = 1627ee6c402SRoman Lebedev InductionDescriptor::isInductionPHI(Inst_i, L, &SE, IndDesc); 1637ee6c402SRoman Lebedev EXPECT_TRUE(IsInductionPHI); 1647ee6c402SRoman Lebedev }); 1657ee6c402SRoman Lebedev } 166d9c52c31SKarthik Senthil 167d9c52c31SKarthik Senthil // This tests that correct identity value is returned for a RecurrenceDescriptor 168d9c52c31SKarthik Senthil // that describes FMin reduction idiom. 169d9c52c31SKarthik Senthil TEST(IVDescriptorsTest, FMinRednIdentity) { 170d9c52c31SKarthik Senthil // Parse the module. 171d9c52c31SKarthik Senthil LLVMContext Context; 172d9c52c31SKarthik Senthil 173d9c52c31SKarthik Senthil std::unique_ptr<Module> M = parseIR(Context, 17439197691SNikita Popov R"(define float @foo(ptr %A, i64 %ub) { 175d9c52c31SKarthik Senthil entry: 176d9c52c31SKarthik Senthil br label %for.body 177d9c52c31SKarthik Senthil 178d9c52c31SKarthik Senthil for.body: 179d9c52c31SKarthik Senthil %i = phi i64 [ 0, %entry ], [ %i.next, %for.body ] 180d9c52c31SKarthik Senthil %fmin = phi float [ 1.000000e+00, %entry ], [ %fmin.next, %for.body ] 18139197691SNikita Popov %arrayidx = getelementptr inbounds float, ptr %A, i64 %i 18239197691SNikita Popov %ld = load float, ptr %arrayidx 183d9c52c31SKarthik Senthil %fmin.cmp = fcmp nnan nsz olt float %fmin, %ld 184d9c52c31SKarthik Senthil %fmin.next = select nnan nsz i1 %fmin.cmp, float %fmin, float %ld 185d9c52c31SKarthik Senthil %i.next = add nsw i64 %i, 1 186d9c52c31SKarthik Senthil %cmp = icmp slt i64 %i.next, %ub 187d9c52c31SKarthik Senthil br i1 %cmp, label %for.body, label %for.end 188d9c52c31SKarthik Senthil 189d9c52c31SKarthik Senthil for.end: 190d9c52c31SKarthik Senthil %fmin.lcssa = phi float [ %fmin.next, %for.body ] 191d9c52c31SKarthik Senthil ret float %fmin.lcssa 192d9c52c31SKarthik Senthil })"); 193d9c52c31SKarthik Senthil 194d9c52c31SKarthik Senthil runWithLoopInfoAndSE( 195d9c52c31SKarthik Senthil *M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { 196d9c52c31SKarthik Senthil Function::iterator FI = F.begin(); 197d9c52c31SKarthik Senthil // First basic block is entry - skip it. 198d9c52c31SKarthik Senthil BasicBlock *Header = &*(++FI); 199d9c52c31SKarthik Senthil assert(Header->getName() == "for.body"); 200d9c52c31SKarthik Senthil Loop *L = LI.getLoopFor(Header); 201d9c52c31SKarthik Senthil EXPECT_NE(L, nullptr); 202d9c52c31SKarthik Senthil BasicBlock::iterator BBI = Header->begin(); 203d9c52c31SKarthik Senthil assert((&*BBI)->getName() == "i"); 204d9c52c31SKarthik Senthil ++BBI; 205d9c52c31SKarthik Senthil PHINode *Phi = dyn_cast<PHINode>(&*BBI); 206d9c52c31SKarthik Senthil assert(Phi->getName() == "fmin"); 207d9c52c31SKarthik Senthil RecurrenceDescriptor Rdx; 208d9c52c31SKarthik Senthil bool IsRdxPhi = RecurrenceDescriptor::isReductionPHI(Phi, L, Rdx); 209d9c52c31SKarthik Senthil EXPECT_TRUE(IsRdxPhi); 210d9c52c31SKarthik Senthil RecurKind Kind = Rdx.getRecurrenceKind(); 211d9c52c31SKarthik Senthil EXPECT_EQ(Kind, RecurKind::FMin); 212d9c52c31SKarthik Senthil }); 213d9c52c31SKarthik Senthil } 214d9c52c31SKarthik Senthil 215d9c52c31SKarthik Senthil // This tests that correct identity value is returned for a RecurrenceDescriptor 216d9c52c31SKarthik Senthil // that describes FMax reduction idiom. 217d9c52c31SKarthik Senthil TEST(IVDescriptorsTest, FMaxRednIdentity) { 218d9c52c31SKarthik Senthil // Parse the module. 219d9c52c31SKarthik Senthil LLVMContext Context; 220d9c52c31SKarthik Senthil 221d9c52c31SKarthik Senthil std::unique_ptr<Module> M = parseIR(Context, 22239197691SNikita Popov R"(define float @foo(ptr %A, i64 %ub) { 223d9c52c31SKarthik Senthil entry: 224d9c52c31SKarthik Senthil br label %for.body 225d9c52c31SKarthik Senthil 226d9c52c31SKarthik Senthil for.body: 227d9c52c31SKarthik Senthil %i = phi i64 [ 0, %entry ], [ %i.next, %for.body ] 228d9c52c31SKarthik Senthil %fmax = phi float [ 1.000000e+00, %entry ], [ %fmax.next, %for.body ] 22939197691SNikita Popov %arrayidx = getelementptr inbounds float, ptr %A, i64 %i 23039197691SNikita Popov %ld = load float, ptr %arrayidx 231d9c52c31SKarthik Senthil %fmax.cmp = fcmp nnan nsz ogt float %fmax, %ld 232d9c52c31SKarthik Senthil %fmax.next = select nnan nsz i1 %fmax.cmp, float %fmax, float %ld 233d9c52c31SKarthik Senthil %i.next = add nsw i64 %i, 1 234d9c52c31SKarthik Senthil %cmp = icmp slt i64 %i.next, %ub 235d9c52c31SKarthik Senthil br i1 %cmp, label %for.body, label %for.end 236d9c52c31SKarthik Senthil 237d9c52c31SKarthik Senthil for.end: 238d9c52c31SKarthik Senthil %fmax.lcssa = phi float [ %fmax.next, %for.body ] 239d9c52c31SKarthik Senthil ret float %fmax.lcssa 240d9c52c31SKarthik Senthil })"); 241d9c52c31SKarthik Senthil 242d9c52c31SKarthik Senthil runWithLoopInfoAndSE( 243d9c52c31SKarthik Senthil *M, "foo", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { 244d9c52c31SKarthik Senthil Function::iterator FI = F.begin(); 245d9c52c31SKarthik Senthil // First basic block is entry - skip it. 246d9c52c31SKarthik Senthil BasicBlock *Header = &*(++FI); 247d9c52c31SKarthik Senthil assert(Header->getName() == "for.body"); 248d9c52c31SKarthik Senthil Loop *L = LI.getLoopFor(Header); 249d9c52c31SKarthik Senthil EXPECT_NE(L, nullptr); 250d9c52c31SKarthik Senthil BasicBlock::iterator BBI = Header->begin(); 251d9c52c31SKarthik Senthil assert((&*BBI)->getName() == "i"); 252d9c52c31SKarthik Senthil ++BBI; 253d9c52c31SKarthik Senthil PHINode *Phi = dyn_cast<PHINode>(&*BBI); 254d9c52c31SKarthik Senthil assert(Phi->getName() == "fmax"); 255d9c52c31SKarthik Senthil RecurrenceDescriptor Rdx; 256d9c52c31SKarthik Senthil bool IsRdxPhi = RecurrenceDescriptor::isReductionPHI(Phi, L, Rdx); 257d9c52c31SKarthik Senthil EXPECT_TRUE(IsRdxPhi); 258d9c52c31SKarthik Senthil RecurKind Kind = Rdx.getRecurrenceKind(); 259d9c52c31SKarthik Senthil EXPECT_EQ(Kind, RecurKind::FMax); 260d9c52c31SKarthik Senthil }); 261d9c52c31SKarthik Senthil } 262