xref: /llvm-project/llvm/unittests/Analysis/IVDescriptorsTest.cpp (revision 3d9abfc9f841b13825e3d03cfba272f5eeab9a3b)
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