diff --git a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h b/llvm/include/llvm/Analysis/ScalarEvolutionExpander.h similarity index 99% rename from llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h rename to llvm/include/llvm/Analysis/ScalarEvolutionExpander.h index d339444d2021..b4d727449fbe 100644 --- a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h +++ b/llvm/include/llvm/Analysis/ScalarEvolutionExpander.h @@ -1,4 +1,4 @@ -//=== Transforms/Utils/ScalarEvolutionExpander.h - SCEV Expander *- C++ -*-===// +//===---- llvm/Analysis/ScalarEvolutionExpander.h - SCEV Exprs --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index 6e9f7de7e940..cc9ff0bc1f57 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -82,6 +82,7 @@ add_llvm_component_library(LLVMAnalysis RegionPrinter.cpp ScalarEvolution.cpp ScalarEvolutionAliasAnalysis.cpp + ScalarEvolutionExpander.cpp ScalarEvolutionNormalization.cpp StackSafetyAnalysis.cpp SyncDependenceAnalysis.cpp diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp index 017dba6c1f65..26fa5112c29a 100644 --- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp +++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp @@ -30,6 +30,7 @@ #include "llvm/Analysis/MemoryLocation.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" @@ -58,7 +59,6 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include #include #include diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Analysis/ScalarEvolutionExpander.cpp similarity index 99% rename from llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp rename to llvm/lib/Analysis/ScalarEvolutionExpander.cpp index fdcea7a555ba..dc5d02aa3a3c 100644 --- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp +++ b/llvm/lib/Analysis/ScalarEvolutionExpander.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Analysis/InstructionSimplify.h" diff --git a/llvm/lib/CodeGen/HardwareLoops.cpp b/llvm/lib/CodeGen/HardwareLoops.cpp index d5212081ff93..65c2a37e5d43 100644 --- a/llvm/lib/CodeGen/HardwareLoops.cpp +++ b/llvm/lib/CodeGen/HardwareLoops.cpp @@ -20,6 +20,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetPassConfig.h" @@ -42,7 +43,6 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #define DEBUG_TYPE "hardware-loops" diff --git a/llvm/lib/Target/ARM/MVETailPredication.cpp b/llvm/lib/Target/ARM/MVETailPredication.cpp index 81fdd804b372..24bbc6236a4e 100644 --- a/llvm/lib/Target/ARM/MVETailPredication.cpp +++ b/llvm/lib/Target/ARM/MVETailPredication.cpp @@ -26,6 +26,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/TargetPassConfig.h" @@ -35,7 +36,6 @@ #include "llvm/IR/PatternMatch.h" #include "llvm/Support/Debug.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" using namespace llvm; diff --git a/llvm/lib/Target/Hexagon/HexagonLoopIdiomRecognition.cpp b/llvm/lib/Target/Hexagon/HexagonLoopIdiomRecognition.cpp index 2c1e0cadd9ee..ffaf71e23690 100644 --- a/llvm/lib/Target/Hexagon/HexagonLoopIdiomRecognition.cpp +++ b/llvm/lib/Target/Hexagon/HexagonLoopIdiomRecognition.cpp @@ -20,6 +20,7 @@ #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/MemoryLocation.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" @@ -56,7 +57,6 @@ #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils.h" #include "llvm/Transforms/Utils/Local.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include #include #include diff --git a/llvm/lib/Target/PowerPC/PPCLoopInstrFormPrep.cpp b/llvm/lib/Target/PowerPC/PPCLoopInstrFormPrep.cpp index fe2706117453..b761f337533b 100644 --- a/llvm/lib/Target/PowerPC/PPCLoopInstrFormPrep.cpp +++ b/llvm/lib/Target/PowerPC/PPCLoopInstrFormPrep.cpp @@ -53,6 +53,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" @@ -73,7 +74,6 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include #include #include diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp index 15766ec6d56f..d8d7acae5c9f 100644 --- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -39,6 +39,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -80,7 +81,6 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/SimplifyIndVar.h" #include #include diff --git a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp index a452552f510c..58469749600e 100644 --- a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp +++ b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp @@ -56,6 +56,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" @@ -86,7 +87,6 @@ #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include #include diff --git a/llvm/lib/Transforms/Scalar/LoopDataPrefetch.cpp b/llvm/lib/Transforms/Scalar/LoopDataPrefetch.cpp index c82de5b56551..ab65f56d088f 100644 --- a/llvm/lib/Transforms/Scalar/LoopDataPrefetch.cpp +++ b/llvm/lib/Transforms/Scalar/LoopDataPrefetch.cpp @@ -21,6 +21,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/CFG.h" @@ -31,7 +32,6 @@ #include "llvm/Support/Debug.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/ValueMapper.h" using namespace llvm; diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp index 81df56458092..b77843d7cd71 100644 --- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp +++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp @@ -53,6 +53,7 @@ #include "llvm/Analysis/MemoryLocation.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -90,7 +91,6 @@ #include "llvm/Transforms/Utils/BuildLibCalls.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include #include #include diff --git a/llvm/lib/Transforms/Scalar/LoopLoadElimination.cpp b/llvm/lib/Transforms/Scalar/LoopLoadElimination.cpp index 72b0f0517af2..4e1b4e87ebc9 100644 --- a/llvm/lib/Transforms/Scalar/LoopLoadElimination.cpp +++ b/llvm/lib/Transforms/Scalar/LoopLoadElimination.cpp @@ -38,6 +38,7 @@ #include "llvm/Analysis/MemorySSA.h" #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -57,7 +58,6 @@ #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils.h" #include "llvm/Transforms/Utils/LoopVersioning.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/SizeOpts.h" #include #include diff --git a/llvm/lib/Transforms/Scalar/LoopPredication.cpp b/llvm/lib/Transforms/Scalar/LoopPredication.cpp index 06fe3ed42475..1a42f6b23443 100644 --- a/llvm/lib/Transforms/Scalar/LoopPredication.cpp +++ b/llvm/lib/Transforms/Scalar/LoopPredication.cpp @@ -184,6 +184,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" @@ -198,7 +199,6 @@ #include "llvm/Transforms/Utils/GuardUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #define DEBUG_TYPE "loop-predication" diff --git a/llvm/lib/Transforms/Scalar/LoopRerollPass.cpp b/llvm/lib/Transforms/Scalar/LoopRerollPass.cpp index d16aade41095..da13a342ae12 100644 --- a/llvm/lib/Transforms/Scalar/LoopRerollPass.cpp +++ b/llvm/lib/Transforms/Scalar/LoopRerollPass.cpp @@ -24,6 +24,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" @@ -54,7 +55,6 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include #include #include diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp index 39cf6f612156..e9f368628a08 100644 --- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp +++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp @@ -70,6 +70,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/ScalarEvolutionNormalization.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -108,7 +109,6 @@ #include "llvm/Transforms/Utils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include #include #include diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt index 8381190e2281..67bc6fbd1809 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -52,7 +52,6 @@ add_llvm_component_library(LLVMTransformUtils SSAUpdater.cpp SSAUpdaterBulk.cpp SanitizerStats.cpp - ScalarEvolutionExpander.cpp SimplifyCFG.cpp SimplifyIndVar.cpp SimplifyLibCalls.cpp diff --git a/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp b/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp index 08d1c748104a..ddb7479924bd 100644 --- a/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp +++ b/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp @@ -25,6 +25,7 @@ #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/LoopIterator.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Metadata.h" @@ -36,7 +37,6 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/LoopUtils.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/UnrollLoop.h" #include diff --git a/llvm/lib/Transforms/Utils/LoopVersioning.cpp b/llvm/lib/Transforms/Utils/LoopVersioning.cpp index 33a26ed5a042..50752bd78a65 100644 --- a/llvm/lib/Transforms/Utils/LoopVersioning.cpp +++ b/llvm/lib/Transforms/Utils/LoopVersioning.cpp @@ -15,13 +15,13 @@ #include "llvm/Transforms/Utils/LoopVersioning.h" #include "llvm/Analysis/LoopAccessAnalysis.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/MDBuilder.h" #include "llvm/InitializePasses.h" #include "llvm/Support/CommandLine.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" using namespace llvm; diff --git a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp index 2d6c462b7587..cbb114f9a47a 100644 --- a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" @@ -26,7 +27,6 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/Local.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" using namespace llvm; diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index daaf883ce012..fd30d52a562a 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -91,6 +91,7 @@ #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" @@ -136,7 +137,6 @@ #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopUtils.h" #include "llvm/Transforms/Utils/LoopVersioning.h" -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" #include "llvm/Transforms/Utils/SizeOpts.h" #include "llvm/Transforms/Vectorize/LoopVectorizationLegality.h" #include diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp index b30eb0146361..c42ebf656dd4 100644 --- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp @@ -9,8 +9,8 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" -#include "llvm/Analysis/ScalarEvolutionNormalization.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Constants.h" @@ -141,6 +141,72 @@ TEST_F(ScalarEvolutionsTest, SimplifiedPHI) { EXPECT_EQ(S1, S2); } +TEST_F(ScalarEvolutionsTest, ExpandPtrTypeSCEV) { + // It is to test the fix for PR30213. It exercises the branch in scev + // expansion when the value in ValueOffsetPair is a ptr and the offset + // is not divisible by the elem type size of value. + auto *I8Ty = Type::getInt8Ty(Context); + auto *I8PtrTy = Type::getInt8PtrTy(Context); + auto *I32Ty = Type::getInt32Ty(Context); + auto *I32PtrTy = Type::getInt32PtrTy(Context); + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), std::vector(), false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M); + BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F); + BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F); + BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F); + BranchInst::Create(LoopBB, EntryBB); + ReturnInst::Create(Context, nullptr, ExitBB); + + // loop: ; preds = %loop, %entry + // %alloca = alloca i32 + // %gep0 = getelementptr i32, i32* %alloca, i32 1 + // %bitcast1 = bitcast i32* %gep0 to i8* + // %gep1 = getelementptr i8, i8* %bitcast1, i32 1 + // %gep2 = getelementptr i8, i8* undef, i32 1 + // %cmp = icmp ult i8* undef, %bitcast1 + // %select = select i1 %cmp, i8* %gep1, i8* %gep2 + // %bitcast2 = bitcast i8* %select to i32* + // br i1 undef, label %loop, label %exit + + const DataLayout &DL = F->getParent()->getDataLayout(); + BranchInst *Br = BranchInst::Create( + LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB); + AllocaInst *Alloca = new AllocaInst(I32Ty, DL.getAllocaAddrSpace(), + "alloca", Br); + ConstantInt *Ci32 = ConstantInt::get(Context, APInt(32, 1)); + GetElementPtrInst *Gep0 = + GetElementPtrInst::Create(I32Ty, Alloca, Ci32, "gep0", Br); + CastInst *CastA = + CastInst::CreateBitOrPointerCast(Gep0, I8PtrTy, "bitcast1", Br); + GetElementPtrInst *Gep1 = + GetElementPtrInst::Create(I8Ty, CastA, Ci32, "gep1", Br); + GetElementPtrInst *Gep2 = GetElementPtrInst::Create( + I8Ty, UndefValue::get(I8PtrTy), Ci32, "gep2", Br); + CmpInst *Cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, + UndefValue::get(I8PtrTy), CastA, "cmp", Br); + SelectInst *Sel = SelectInst::Create(Cmp, Gep1, Gep2, "select", Br); + CastInst *CastB = + CastInst::CreateBitOrPointerCast(Sel, I32PtrTy, "bitcast2", Br); + + ScalarEvolution SE = buildSE(*F); + auto *S = SE.getSCEV(CastB); + SCEVExpander Exp(SE, M.getDataLayout(), "expander"); + Value *V = + Exp.expandCodeFor(cast(S)->getOperand(1), nullptr, Br); + + // Expect the expansion code contains: + // %0 = bitcast i32* %bitcast2 to i8* + // %uglygep = getelementptr i8, i8* %0, i64 -1 + // %1 = bitcast i8* %uglygep to i32* + EXPECT_TRUE(isa(V)); + Instruction *Gep = cast(V)->getPrevNode(); + EXPECT_TRUE(isa(Gep)); + EXPECT_TRUE(isa(Gep->getOperand(1))); + EXPECT_EQ(cast(Gep->getOperand(1))->getSExtValue(), -1); + EXPECT_TRUE(isa(Gep->getPrevNode())); +} + static Instruction *getInstructionByName(Function &F, StringRef Name) { for (auto &I : instructions(F)) if (I.getName() == Name) @@ -653,6 +719,83 @@ TEST_F(ScalarEvolutionsTest, SCEVZeroExtendExpr) { SE.getZeroExtendExpr(S, I128Ty); } +// Make sure that SCEV doesn't introduce illegal ptrtoint/inttoptr instructions +TEST_F(ScalarEvolutionsTest, SCEVZeroExtendExprNonIntegral) { + /* + * Create the following code: + * func(i64 addrspace(10)* %arg) + * top: + * br label %L.ph + * L.ph: + * br label %L + * L: + * %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ] + * %add = add i64 %phi2, 1 + * br i1 undef, label %post, label %L2 + * post: + * %gepbase = getelementptr i64 addrspace(10)* %arg, i64 1 + * #= %gep = getelementptr i64 addrspace(10)* %gepbase, i64 %add =# + * ret void + * + * We will create the appropriate SCEV expression for %gep and expand it, + * then check that no inttoptr/ptrtoint instructions got inserted. + */ + + // Create a module with non-integral pointers in it's datalayout + Module NIM("nonintegral", Context); + std::string DataLayout = M.getDataLayoutStr(); + if (!DataLayout.empty()) + DataLayout += "-"; + DataLayout += "ni:10"; + NIM.setDataLayout(DataLayout); + + Type *T_int1 = Type::getInt1Ty(Context); + Type *T_int64 = Type::getInt64Ty(Context); + Type *T_pint64 = T_int64->getPointerTo(10); + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM); + + Argument *Arg = &*F->arg_begin(); + + BasicBlock *Top = BasicBlock::Create(Context, "top", F); + BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F); + BasicBlock *L = BasicBlock::Create(Context, "L", F); + BasicBlock *Post = BasicBlock::Create(Context, "post", F); + + IRBuilder<> Builder(Top); + Builder.CreateBr(LPh); + + Builder.SetInsertPoint(LPh); + Builder.CreateBr(L); + + Builder.SetInsertPoint(L); + PHINode *Phi = Builder.CreatePHI(T_int64, 2); + Value *Add = Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add"); + Builder.CreateCondBr(UndefValue::get(T_int1), L, Post); + Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh); + Phi->addIncoming(Add, L); + + Builder.SetInsertPoint(Post); + Value *GepBase = + Builder.CreateGEP(T_int64, Arg, ConstantInt::get(T_int64, 1)); + Instruction *Ret = Builder.CreateRetVoid(); + + ScalarEvolution SE = buildSE(*F); + auto *AddRec = + SE.getAddRecExpr(SE.getUnknown(GepBase), SE.getConstant(T_int64, 1), + LI->getLoopFor(L), SCEV::FlagNUW); + + SCEVExpander Exp(SE, NIM.getDataLayout(), "expander"); + Exp.disableCanonicalMode(); + Exp.expandCodeFor(AddRec, T_pint64, Ret); + + // Make sure none of the instructions inserted were inttoptr/ptrtoint. + // The verifier will check this. + EXPECT_FALSE(verifyFunction(*F, &errs())); +} + // Make sure that SCEV invalidates exit limits after invalidating the values it // depends on when we forget a loop. TEST_F(ScalarEvolutionsTest, SCEVExitLimitForgetLoop) { @@ -979,6 +1122,283 @@ TEST_F(ScalarEvolutionsTest, SCEVFoldSumOfTruncs) { EXPECT_EQ(Expr, ZeroConst); } +// Check that we can correctly identify the points at which the SCEV of the +// AddRec can be expanded. +TEST_F(ScalarEvolutionsTest, SCEVExpanderIsSafeToExpandAt) { + /* + * Create the following code: + * func(i64 addrspace(10)* %arg) + * top: + * br label %L.ph + * L.ph: + * br label %L + * L: + * %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ] + * %add = add i64 %phi2, 1 + * %cond = icmp slt i64 %add, 1000; then becomes 2000. + * br i1 %cond, label %post, label %L2 + * post: + * ret void + * + */ + + // Create a module with non-integral pointers in it's datalayout + Module NIM("nonintegral", Context); + std::string DataLayout = M.getDataLayoutStr(); + if (!DataLayout.empty()) + DataLayout += "-"; + DataLayout += "ni:10"; + NIM.setDataLayout(DataLayout); + + Type *T_int64 = Type::getInt64Ty(Context); + Type *T_pint64 = T_int64->getPointerTo(10); + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM); + + BasicBlock *Top = BasicBlock::Create(Context, "top", F); + BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F); + BasicBlock *L = BasicBlock::Create(Context, "L", F); + BasicBlock *Post = BasicBlock::Create(Context, "post", F); + + IRBuilder<> Builder(Top); + Builder.CreateBr(LPh); + + Builder.SetInsertPoint(LPh); + Builder.CreateBr(L); + + Builder.SetInsertPoint(L); + PHINode *Phi = Builder.CreatePHI(T_int64, 2); + auto *Add = cast( + Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add")); + auto *Limit = ConstantInt::get(T_int64, 1000); + auto *Cond = cast( + Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, Limit, "cond")); + Builder.CreateCondBr(Cond, L, Post); + Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh); + Phi->addIncoming(Add, L); + + Builder.SetInsertPoint(Post); + Builder.CreateRetVoid(); + + ScalarEvolution SE = buildSE(*F); + const SCEV *S = SE.getSCEV(Phi); + EXPECT_TRUE(isa(S)); + const SCEVAddRecExpr *AR = cast(S); + EXPECT_TRUE(AR->isAffine()); + EXPECT_FALSE(isSafeToExpandAt(AR, Top->getTerminator(), SE)); + EXPECT_FALSE(isSafeToExpandAt(AR, LPh->getTerminator(), SE)); + EXPECT_TRUE(isSafeToExpandAt(AR, L->getTerminator(), SE)); + EXPECT_TRUE(isSafeToExpandAt(AR, Post->getTerminator(), SE)); +} + +// Check that SCEV expander does not use the nuw instruction +// for expansion. +TEST_F(ScalarEvolutionsTest, SCEVExpanderNUW) { + /* + * Create the following code: + * func(i64 %a) + * entry: + * br false, label %exit, label %body + * body: + * %s1 = add i64 %a, -1 + * br label %exit + * exit: + * %s = add nuw i64 %a, -1 + * ret %s + */ + + // Create a module. + Module M("SCEVExpanderNUW", Context); + + Type *T_int64 = Type::getInt64Ty(Context); + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), { T_int64 }, false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); + Argument *Arg = &*F->arg_begin(); + ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); + + BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); + BasicBlock *Body = BasicBlock::Create(Context, "body", F); + BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); + + IRBuilder<> Builder(Entry); + ConstantInt *Cond = ConstantInt::get(Context, APInt(1, 0)); + Builder.CreateCondBr(Cond, Exit, Body); + + Builder.SetInsertPoint(Body); + auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); + Builder.CreateBr(Exit); + + Builder.SetInsertPoint(Exit); + auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); + S2->setHasNoUnsignedWrap(true); + auto *R = cast(Builder.CreateRetVoid()); + + ScalarEvolution SE = buildSE(*F); + const SCEV *S = SE.getSCEV(S1); + EXPECT_TRUE(isa(S)); + SCEVExpander Exp(SE, M.getDataLayout(), "expander"); + auto *I = cast(Exp.expandCodeFor(S, nullptr, R)); + EXPECT_FALSE(I->hasNoUnsignedWrap()); +} + +// Check that SCEV expander does not use the nsw instruction +// for expansion. +TEST_F(ScalarEvolutionsTest, SCEVExpanderNSW) { + /* + * Create the following code: + * func(i64 %a) + * entry: + * br false, label %exit, label %body + * body: + * %s1 = add i64 %a, -1 + * br label %exit + * exit: + * %s = add nsw i64 %a, -1 + * ret %s + */ + + // Create a module. + Module M("SCEVExpanderNSW", Context); + + Type *T_int64 = Type::getInt64Ty(Context); + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), { T_int64 }, false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); + Argument *Arg = &*F->arg_begin(); + ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); + + BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); + BasicBlock *Body = BasicBlock::Create(Context, "body", F); + BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); + + IRBuilder<> Builder(Entry); + ConstantInt *Cond = ConstantInt::get(Context, APInt(1, 0)); + Builder.CreateCondBr(Cond, Exit, Body); + + Builder.SetInsertPoint(Body); + auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); + Builder.CreateBr(Exit); + + Builder.SetInsertPoint(Exit); + auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); + S2->setHasNoSignedWrap(true); + auto *R = cast(Builder.CreateRetVoid()); + + ScalarEvolution SE = buildSE(*F); + const SCEV *S = SE.getSCEV(S1); + EXPECT_TRUE(isa(S)); + SCEVExpander Exp(SE, M.getDataLayout(), "expander"); + auto *I = cast(Exp.expandCodeFor(S, nullptr, R)); + EXPECT_FALSE(I->hasNoSignedWrap()); +} + +// Check that SCEV does not save the SCEV -> V +// mapping of SCEV differ from V in NUW flag. +TEST_F(ScalarEvolutionsTest, SCEVCacheNUW) { + /* + * Create the following code: + * func(i64 %a) + * entry: + * %s1 = add i64 %a, -1 + * %s2 = add nuw i64 %a, -1 + * br label %exit + * exit: + * ret %s + */ + + // Create a module. + Module M("SCEVCacheNUW", Context); + + Type *T_int64 = Type::getInt64Ty(Context); + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), { T_int64 }, false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); + Argument *Arg = &*F->arg_begin(); + ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); + + BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); + BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); + + IRBuilder<> Builder(Entry); + auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); + auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); + S2->setHasNoUnsignedWrap(true); + Builder.CreateBr(Exit); + + Builder.SetInsertPoint(Exit); + auto *R = cast(Builder.CreateRetVoid()); + + ScalarEvolution SE = buildSE(*F); + // Get S2 first to move it to cache. + const SCEV *SC2 = SE.getSCEV(S2); + EXPECT_TRUE(isa(SC2)); + // Now get S1. + const SCEV *SC1 = SE.getSCEV(S1); + EXPECT_TRUE(isa(SC1)); + // Expand for S1, it should use S1 not S2 in spite S2 + // first in the cache. + SCEVExpander Exp(SE, M.getDataLayout(), "expander"); + auto *I = cast(Exp.expandCodeFor(SC1, nullptr, R)); + EXPECT_FALSE(I->hasNoUnsignedWrap()); +} + +// Check that SCEV does not save the SCEV -> V +// mapping of SCEV differ from V in NSW flag. +TEST_F(ScalarEvolutionsTest, SCEVCacheNSW) { + /* + * Create the following code: + * func(i64 %a) + * entry: + * %s1 = add i64 %a, -1 + * %s2 = add nsw i64 %a, -1 + * br label %exit + * exit: + * ret %s + */ + + // Create a module. + Module M("SCEVCacheNUW", Context); + + Type *T_int64 = Type::getInt64Ty(Context); + + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), { T_int64 }, false); + Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); + Argument *Arg = &*F->arg_begin(); + ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); + + BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); + BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); + + IRBuilder<> Builder(Entry); + auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); + auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); + S2->setHasNoSignedWrap(true); + Builder.CreateBr(Exit); + + Builder.SetInsertPoint(Exit); + auto *R = cast(Builder.CreateRetVoid()); + + ScalarEvolution SE = buildSE(*F); + // Get S2 first to move it to cache. + const SCEV *SC2 = SE.getSCEV(S2); + EXPECT_TRUE(isa(SC2)); + // Now get S1. + const SCEV *SC1 = SE.getSCEV(S1); + EXPECT_TRUE(isa(SC1)); + // Expand for S1, it should use S1 not S2 in spite S2 + // first in the cache. + SCEVExpander Exp(SE, M.getDataLayout(), "expander"); + auto *I = cast(Exp.expandCodeFor(SC1, nullptr, R)); + EXPECT_FALSE(I->hasNoSignedWrap()); +} + // Check logic of SCEV expression size computation. TEST_F(ScalarEvolutionsTest, SCEVComputeExpressionSize) { /* @@ -1029,6 +1449,240 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeExpressionSize) { EXPECT_EQ(S2S->getExpressionSize(), 5u); } +TEST_F(ScalarEvolutionsTest, SCEVExpandInsertCanonicalIV) { + LLVMContext C; + SMDiagnostic Err; + + // Expand the addrec produced by GetAddRec into a loop without a canonical IV. + // SCEVExpander will insert one. + auto TestNoCanonicalIV = [&]( + std::function GetAddRec) { + std::unique_ptr M = + parseAssemblyString("define i32 @test(i32 %limit) { " + "entry: " + " br label %loop " + "loop: " + " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " + " %i.inc = add nsw i32 %i, 1 " + " %cont = icmp slt i32 %i.inc, %limit " + " br i1 %cont, label %loop, label %exit " + "exit: " + " ret i32 %i.inc " + "}", + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I = GetInstByName(F, "i"); + auto *Loop = LI.getLoopFor(I.getParent()); + EXPECT_FALSE(Loop->getCanonicalInductionVariable()); + + auto *AR = GetAddRec(SE, Loop); + unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType()); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *InsertAt = I.getNextNode(); + Exp.expandCodeFor(AR, nullptr, InsertAt); + PHINode *CanonicalIV = Loop->getCanonicalInductionVariable(); + unsigned CanonicalIVBitWidth = + cast(CanonicalIV->getType())->getBitWidth(); + EXPECT_EQ(CanonicalIVBitWidth, ExpectedCanonicalIVWidth); + }); + }; + + // Expand the addrec produced by GetAddRec into a loop with a canonical IV + // which is narrower than addrec type. + // SCEVExpander will insert a canonical IV of a wider type to expand the + // addrec. + auto TestNarrowCanonicalIV = [&]( + std::function GetAddRec) { + std::unique_ptr M = parseAssemblyString( + "define i32 @test(i32 %limit) { " + "entry: " + " br label %loop " + "loop: " + " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " + " %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] " + " %i.inc = add nsw i32 %i, 1 " + " %canonical.iv.inc = add i8 %canonical.iv, 1 " + " %cont = icmp slt i32 %i.inc, %limit " + " br i1 %cont, label %loop, label %exit " + "exit: " + " ret i32 %i.inc " + "}", + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I = GetInstByName(F, "i"); + + auto *LoopHeaderBB = I.getParent(); + auto *Loop = LI.getLoopFor(LoopHeaderBB); + PHINode *CanonicalIV = Loop->getCanonicalInductionVariable(); + EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv")); + + auto *AR = GetAddRec(SE, Loop); + + unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType()); + unsigned CanonicalIVBitWidth = + cast(CanonicalIV->getType())->getBitWidth(); + EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *InsertAt = I.getNextNode(); + Exp.expandCodeFor(AR, nullptr, InsertAt); + + // Loop over all of the PHI nodes, looking for the new canonical indvar. + PHINode *NewCanonicalIV = nullptr; + for (BasicBlock::iterator i = LoopHeaderBB->begin(); isa(i); + ++i) { + PHINode *PN = cast(i); + if (PN == &I || PN == CanonicalIV) + continue; + // We expect that the only PHI added is the new canonical IV + EXPECT_FALSE(NewCanonicalIV); + NewCanonicalIV = PN; + } + + // Check that NewCanonicalIV is a canonical IV, i.e {0,+,1} + BasicBlock *Incoming = nullptr, *Backedge = nullptr; + EXPECT_TRUE(Loop->getIncomingAndBackEdge(Incoming, Backedge)); + auto *Start = NewCanonicalIV->getIncomingValueForBlock(Incoming); + EXPECT_TRUE(isa(Start)); + EXPECT_TRUE(dyn_cast(Start)->isZero()); + auto *Next = NewCanonicalIV->getIncomingValueForBlock(Backedge); + EXPECT_TRUE(isa(Next)); + auto *NextBinOp = dyn_cast(Next); + EXPECT_EQ(NextBinOp->getOpcode(), Instruction::Add); + EXPECT_EQ(NextBinOp->getOperand(0), NewCanonicalIV); + auto *Step = NextBinOp->getOperand(1); + EXPECT_TRUE(isa(Step)); + EXPECT_TRUE(dyn_cast(Step)->isOne()); + + unsigned NewCanonicalIVBitWidth = + cast(NewCanonicalIV->getType())->getBitWidth(); + EXPECT_EQ(NewCanonicalIVBitWidth, ExpectedCanonicalIVWidth); + }); + }; + + // Expand the addrec produced by GetAddRec into a loop with a canonical IV + // of addrec width. + // To expand the addrec SCEVExpander should use the existing canonical IV. + auto TestMatchingCanonicalIV = [&]( + std::function GetAddRec, + unsigned ARBitWidth) { + auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth); + std::unique_ptr M = parseAssemblyString( + "define i32 @test(i32 %limit) { " + "entry: " + " br label %loop " + "loop: " + " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " + " %canonical.iv = phi " + ARBitWidthTypeStr + + " [ 0, %entry ], [ %canonical.iv.inc, %loop ] " + " %i.inc = add nsw i32 %i, 1 " + " %canonical.iv.inc = add " + ARBitWidthTypeStr + + " %canonical.iv, 1 " + " %cont = icmp slt i32 %i.inc, %limit " + " br i1 %cont, label %loop, label %exit " + "exit: " + " ret i32 %i.inc " + "}", + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I = GetInstByName(F, "i"); + auto &CanonicalIV = GetInstByName(F, "canonical.iv"); + + auto *LoopHeaderBB = I.getParent(); + auto *Loop = LI.getLoopFor(LoopHeaderBB); + EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable()); + unsigned CanonicalIVBitWidth = + cast(CanonicalIV.getType())->getBitWidth(); + + auto *AR = GetAddRec(SE, Loop); + EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType())); + EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *InsertAt = I.getNextNode(); + Exp.expandCodeFor(AR, nullptr, InsertAt); + + // Loop over all of the PHI nodes, looking if a new canonical indvar was + // introduced. + PHINode *NewCanonicalIV = nullptr; + for (BasicBlock::iterator i = LoopHeaderBB->begin(); isa(i); + ++i) { + PHINode *PN = cast(i); + if (PN == &I || PN == &CanonicalIV) + continue; + NewCanonicalIV = PN; + } + EXPECT_FALSE(NewCanonicalIV); + }); + }; + + unsigned ARBitWidth = 16; + Type *ARType = IntegerType::get(C, ARBitWidth); + + // Expand {5,+,1} + auto GetAR2 = [&](ScalarEvolution &SE, Loop *L) -> const SCEV * { + return SE.getAddRecExpr(SE.getConstant(APInt(ARBitWidth, 5)), + SE.getOne(ARType), L, SCEV::FlagAnyWrap); + }; + TestNoCanonicalIV(GetAR2); + TestNarrowCanonicalIV(GetAR2); + TestMatchingCanonicalIV(GetAR2, ARBitWidth); +} + +TEST_F(ScalarEvolutionsTest, SCEVExpanderShlNSW) { + + auto checkOneCase = [this](std::string &&str) { + LLVMContext C; + SMDiagnostic Err; + std::unique_ptr M = parseAssemblyString(str, Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + Function *F = M->getFunction("f"); + ASSERT_NE(F, nullptr) << "Could not find function 'f'"; + + BasicBlock &Entry = F->getEntryBlock(); + LoadInst *Load = cast(&Entry.front()); + BinaryOperator *And = cast(*Load->user_begin()); + + ScalarEvolution SE = buildSE(*F); + const SCEV *AndSCEV = SE.getSCEV(And); + EXPECT_TRUE(isa(AndSCEV)); + EXPECT_TRUE(cast(AndSCEV)->hasNoSignedWrap()); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *I = cast(Exp.expandCodeFor(AndSCEV, nullptr, And)); + EXPECT_EQ(I->getOpcode(), Instruction::Shl); + EXPECT_FALSE(I->hasNoSignedWrap()); + }; + + checkOneCase("define void @f(i16* %arrayidx) { " + " %1 = load i16, i16* %arrayidx " + " %2 = and i16 %1, -32768 " + " ret void " + "} "); + + checkOneCase("define void @f(i8* %arrayidx) { " + " %1 = load i8, i8* %arrayidx " + " %2 = and i8 %1, -128 " + " ret void " + "} "); +} + TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { LLVMContext C; SMDiagnostic Err; @@ -1091,4 +1745,186 @@ TEST_F(ScalarEvolutionsTest, SCEVComputeConstantDifference) { }); } +// Test expansion of nested addrecs in CanonicalMode. +// Expanding nested addrecs in canonical mode requiers a canonical IV of a +// type wider than the type of the addrec itself. Currently, SCEVExpander +// just falls back to literal mode for nested addrecs. +TEST_F(ScalarEvolutionsTest, SCEVExpandNonAffineAddRec) { + LLVMContext C; + SMDiagnostic Err; + + // Expand the addrec produced by GetAddRec into a loop without a canonical IV. + auto TestNoCanonicalIV = [&](std::function GetAddRec) { + std::unique_ptr M = + parseAssemblyString("define i32 @test(i32 %limit) { " + "entry: " + " br label %loop " + "loop: " + " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " + " %i.inc = add nsw i32 %i, 1 " + " %cont = icmp slt i32 %i.inc, %limit " + " br i1 %cont, label %loop, label %exit " + "exit: " + " ret i32 %i.inc " + "}", + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I = GetInstByName(F, "i"); + auto *Loop = LI.getLoopFor(I.getParent()); + EXPECT_FALSE(Loop->getCanonicalInductionVariable()); + + auto *AR = GetAddRec(SE, Loop); + EXPECT_FALSE(AR->isAffine()); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *InsertAt = I.getNextNode(); + Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt); + auto *ExpandedAR = SE.getSCEV(V); + // Check that the expansion happened literally. + EXPECT_EQ(AR, ExpandedAR); + }); + }; + + // Expand the addrec produced by GetAddRec into a loop with a canonical IV + // which is narrower than addrec type. + auto TestNarrowCanonicalIV = [&]( + std::function + GetAddRec) { + std::unique_ptr M = parseAssemblyString( + "define i32 @test(i32 %limit) { " + "entry: " + " br label %loop " + "loop: " + " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " + " %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] " + " %i.inc = add nsw i32 %i, 1 " + " %canonical.iv.inc = add i8 %canonical.iv, 1 " + " %cont = icmp slt i32 %i.inc, %limit " + " br i1 %cont, label %loop, label %exit " + "exit: " + " ret i32 %i.inc " + "}", + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I = GetInstByName(F, "i"); + + auto *LoopHeaderBB = I.getParent(); + auto *Loop = LI.getLoopFor(LoopHeaderBB); + PHINode *CanonicalIV = Loop->getCanonicalInductionVariable(); + EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv")); + + auto *AR = GetAddRec(SE, Loop); + EXPECT_FALSE(AR->isAffine()); + + unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType()); + unsigned CanonicalIVBitWidth = + cast(CanonicalIV->getType())->getBitWidth(); + EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *InsertAt = I.getNextNode(); + Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt); + auto *ExpandedAR = SE.getSCEV(V); + // Check that the expansion happened literally. + EXPECT_EQ(AR, ExpandedAR); + }); + }; + + // Expand the addrec produced by GetAddRec into a loop with a canonical IV + // of addrec width. + auto TestMatchingCanonicalIV = [&]( + std::function + GetAddRec, + unsigned ARBitWidth) { + auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth); + std::unique_ptr M = parseAssemblyString( + "define i32 @test(i32 %limit) { " + "entry: " + " br label %loop " + "loop: " + " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " + " %canonical.iv = phi " + ARBitWidthTypeStr + + " [ 0, %entry ], [ %canonical.iv.inc, %loop ] " + " %i.inc = add nsw i32 %i, 1 " + " %canonical.iv.inc = add " + ARBitWidthTypeStr + + " %canonical.iv, 1 " + " %cont = icmp slt i32 %i.inc, %limit " + " br i1 %cont, label %loop, label %exit " + "exit: " + " ret i32 %i.inc " + "}", + Err, C); + + assert(M && "Could not parse module?"); + assert(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + auto &I = GetInstByName(F, "i"); + auto &CanonicalIV = GetInstByName(F, "canonical.iv"); + + auto *LoopHeaderBB = I.getParent(); + auto *Loop = LI.getLoopFor(LoopHeaderBB); + EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable()); + unsigned CanonicalIVBitWidth = + cast(CanonicalIV.getType())->getBitWidth(); + + auto *AR = GetAddRec(SE, Loop); + EXPECT_FALSE(AR->isAffine()); + EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType())); + EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth); + + SCEVExpander Exp(SE, M->getDataLayout(), "expander"); + auto *InsertAt = I.getNextNode(); + Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt); + auto *ExpandedAR = SE.getSCEV(V); + // Check that the expansion happened literally. + EXPECT_EQ(AR, ExpandedAR); + }); + }; + + unsigned ARBitWidth = 16; + Type *ARType = IntegerType::get(C, ARBitWidth); + + // Expand {5,+,1,+,1} + auto GetAR3 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * { + SmallVector Ops = {SE.getConstant(APInt(ARBitWidth, 5)), + SE.getOne(ARType), SE.getOne(ARType)}; + return cast(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap)); + }; + TestNoCanonicalIV(GetAR3); + TestNarrowCanonicalIV(GetAR3); + TestMatchingCanonicalIV(GetAR3, ARBitWidth); + + // Expand {5,+,1,+,1,+,1} + auto GetAR4 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * { + SmallVector Ops = {SE.getConstant(APInt(ARBitWidth, 5)), + SE.getOne(ARType), SE.getOne(ARType), + SE.getOne(ARType)}; + return cast(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap)); + }; + TestNoCanonicalIV(GetAR4); + TestNarrowCanonicalIV(GetAR4); + TestMatchingCanonicalIV(GetAR4, ARBitWidth); + + // Expand {5,+,1,+,1,+,1,+,1} + auto GetAR5 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * { + SmallVector Ops = {SE.getConstant(APInt(ARBitWidth, 5)), + SE.getOne(ARType), SE.getOne(ARType), + SE.getOne(ARType), SE.getOne(ARType)}; + return cast(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap)); + }; + TestNoCanonicalIV(GetAR5); + TestNarrowCanonicalIV(GetAR5); + TestMatchingCanonicalIV(GetAR5, ARBitWidth); +} + } // end namespace llvm diff --git a/llvm/unittests/Transforms/Utils/CMakeLists.txt b/llvm/unittests/Transforms/Utils/CMakeLists.txt index fd19fad586f5..424f09025ea2 100644 --- a/llvm/unittests/Transforms/Utils/CMakeLists.txt +++ b/llvm/unittests/Transforms/Utils/CMakeLists.txt @@ -17,7 +17,6 @@ add_llvm_unittest(UtilsTests LocalTest.cpp LoopUtilsTest.cpp SizeOptsTest.cpp - ScalarEvolutionExpanderTest.cpp SSAUpdaterBulkTest.cpp UnrollLoopTest.cpp ValueMapperTest.cpp diff --git a/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp b/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp index 953eaa7ac83f..887c9c955821 100644 --- a/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp +++ b/llvm/unittests/Transforms/Utils/CodeMoverUtilsTest.cpp @@ -11,7 +11,7 @@ #include "llvm/Analysis/DependenceAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/PostDominators.h" -#include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/Analysis/ScalarEvolutionExpander.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/LLVMContext.h" diff --git a/llvm/unittests/Transforms/Utils/ScalarEvolutionExpanderTest.cpp b/llvm/unittests/Transforms/Utils/ScalarEvolutionExpanderTest.cpp deleted file mode 100644 index e5fda11cb1ca..000000000000 --- a/llvm/unittests/Transforms/Utils/ScalarEvolutionExpanderTest.cpp +++ /dev/null @@ -1,923 +0,0 @@ -//===- ScalarEvolutionExpanderTest.cpp - ScalarEvolution unit tests -------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Analysis/AssumptionCache.h" -#include "llvm/Analysis/LoopInfo.h" -#include "llvm/Analysis/ScalarEvolutionExpressions.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/AsmParser/Parser.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/SourceMgr.h" -#include "gtest/gtest.h" - -namespace llvm { - -// We use this fixture to ensure that we clean up ScalarEvolution before -// deleting the PassManager. -class ScalarEvolutionsTest : public testing::Test { -protected: - LLVMContext Context; - Module M; - TargetLibraryInfoImpl TLII; - TargetLibraryInfo TLI; - - std::unique_ptr AC; - std::unique_ptr DT; - std::unique_ptr LI; - - ScalarEvolutionsTest() : M("", Context), TLII(), TLI(TLII) {} - - ScalarEvolution buildSE(Function &F) { - AC.reset(new AssumptionCache(F)); - DT.reset(new DominatorTree(F)); - LI.reset(new LoopInfo(*DT)); - return ScalarEvolution(F, TLI, *AC, *DT, *LI); - } - - void runWithSE( - Module &M, StringRef FuncName, - function_ref Test) { - auto *F = M.getFunction(FuncName); - ASSERT_NE(F, nullptr) << "Could not find " << FuncName; - ScalarEvolution SE = buildSE(*F); - Test(*F, *LI, SE); - } - - static Optional computeConstantDifference(ScalarEvolution &SE, - const SCEV *LHS, - const SCEV *RHS) { - return SE.computeConstantDifference(LHS, RHS); - } -}; - -static Instruction &GetInstByName(Function &F, StringRef Name) { - for (auto &I : instructions(F)) - if (I.getName() == Name) - return I; - llvm_unreachable("Could not find instructions!"); -} - -TEST_F(ScalarEvolutionsTest, ExpandPtrTypeSCEV) { - // It is to test the fix for PR30213. It exercises the branch in scev - // expansion when the value in ValueOffsetPair is a ptr and the offset - // is not divisible by the elem type size of value. - auto *I8Ty = Type::getInt8Ty(Context); - auto *I8PtrTy = Type::getInt8PtrTy(Context); - auto *I32Ty = Type::getInt32Ty(Context); - auto *I32PtrTy = Type::getInt32PtrTy(Context); - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), std::vector(), false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "f", M); - BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F); - BasicBlock *LoopBB = BasicBlock::Create(Context, "loop", F); - BasicBlock *ExitBB = BasicBlock::Create(Context, "exit", F); - BranchInst::Create(LoopBB, EntryBB); - ReturnInst::Create(Context, nullptr, ExitBB); - - // loop: ; preds = %loop, %entry - // %alloca = alloca i32 - // %gep0 = getelementptr i32, i32* %alloca, i32 1 - // %bitcast1 = bitcast i32* %gep0 to i8* - // %gep1 = getelementptr i8, i8* %bitcast1, i32 1 - // %gep2 = getelementptr i8, i8* undef, i32 1 - // %cmp = icmp ult i8* undef, %bitcast1 - // %select = select i1 %cmp, i8* %gep1, i8* %gep2 - // %bitcast2 = bitcast i8* %select to i32* - // br i1 undef, label %loop, label %exit - - const DataLayout &DL = F->getParent()->getDataLayout(); - BranchInst *Br = BranchInst::Create( - LoopBB, ExitBB, UndefValue::get(Type::getInt1Ty(Context)), LoopBB); - AllocaInst *Alloca = - new AllocaInst(I32Ty, DL.getAllocaAddrSpace(), "alloca", Br); - ConstantInt *Ci32 = ConstantInt::get(Context, APInt(32, 1)); - GetElementPtrInst *Gep0 = - GetElementPtrInst::Create(I32Ty, Alloca, Ci32, "gep0", Br); - CastInst *CastA = - CastInst::CreateBitOrPointerCast(Gep0, I8PtrTy, "bitcast1", Br); - GetElementPtrInst *Gep1 = - GetElementPtrInst::Create(I8Ty, CastA, Ci32, "gep1", Br); - GetElementPtrInst *Gep2 = GetElementPtrInst::Create( - I8Ty, UndefValue::get(I8PtrTy), Ci32, "gep2", Br); - CmpInst *Cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, - UndefValue::get(I8PtrTy), CastA, "cmp", Br); - SelectInst *Sel = SelectInst::Create(Cmp, Gep1, Gep2, "select", Br); - CastInst *CastB = - CastInst::CreateBitOrPointerCast(Sel, I32PtrTy, "bitcast2", Br); - - ScalarEvolution SE = buildSE(*F); - auto *S = SE.getSCEV(CastB); - SCEVExpander Exp(SE, M.getDataLayout(), "expander"); - Value *V = - Exp.expandCodeFor(cast(S)->getOperand(1), nullptr, Br); - - // Expect the expansion code contains: - // %0 = bitcast i32* %bitcast2 to i8* - // %uglygep = getelementptr i8, i8* %0, i64 -1 - // %1 = bitcast i8* %uglygep to i32* - EXPECT_TRUE(isa(V)); - Instruction *Gep = cast(V)->getPrevNode(); - EXPECT_TRUE(isa(Gep)); - EXPECT_TRUE(isa(Gep->getOperand(1))); - EXPECT_EQ(cast(Gep->getOperand(1))->getSExtValue(), -1); - EXPECT_TRUE(isa(Gep->getPrevNode())); -} - -// Make sure that SCEV doesn't introduce illegal ptrtoint/inttoptr instructions -TEST_F(ScalarEvolutionsTest, SCEVZeroExtendExprNonIntegral) { - /* - * Create the following code: - * func(i64 addrspace(10)* %arg) - * top: - * br label %L.ph - * L.ph: - * br label %L - * L: - * %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ] - * %add = add i64 %phi2, 1 - * br i1 undef, label %post, label %L2 - * post: - * %gepbase = getelementptr i64 addrspace(10)* %arg, i64 1 - * #= %gep = getelementptr i64 addrspace(10)* %gepbase, i64 %add =# - * ret void - * - * We will create the appropriate SCEV expression for %gep and expand it, - * then check that no inttoptr/ptrtoint instructions got inserted. - */ - - // Create a module with non-integral pointers in it's datalayout - Module NIM("nonintegral", Context); - std::string DataLayout = M.getDataLayoutStr(); - if (!DataLayout.empty()) - DataLayout += "-"; - DataLayout += "ni:10"; - NIM.setDataLayout(DataLayout); - - Type *T_int1 = Type::getInt1Ty(Context); - Type *T_int64 = Type::getInt64Ty(Context); - Type *T_pint64 = T_int64->getPointerTo(10); - - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM); - - Argument *Arg = &*F->arg_begin(); - - BasicBlock *Top = BasicBlock::Create(Context, "top", F); - BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F); - BasicBlock *L = BasicBlock::Create(Context, "L", F); - BasicBlock *Post = BasicBlock::Create(Context, "post", F); - - IRBuilder<> Builder(Top); - Builder.CreateBr(LPh); - - Builder.SetInsertPoint(LPh); - Builder.CreateBr(L); - - Builder.SetInsertPoint(L); - PHINode *Phi = Builder.CreatePHI(T_int64, 2); - Value *Add = Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add"); - Builder.CreateCondBr(UndefValue::get(T_int1), L, Post); - Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh); - Phi->addIncoming(Add, L); - - Builder.SetInsertPoint(Post); - Value *GepBase = - Builder.CreateGEP(T_int64, Arg, ConstantInt::get(T_int64, 1)); - Instruction *Ret = Builder.CreateRetVoid(); - - ScalarEvolution SE = buildSE(*F); - auto *AddRec = - SE.getAddRecExpr(SE.getUnknown(GepBase), SE.getConstant(T_int64, 1), - LI->getLoopFor(L), SCEV::FlagNUW); - - SCEVExpander Exp(SE, NIM.getDataLayout(), "expander"); - Exp.disableCanonicalMode(); - Exp.expandCodeFor(AddRec, T_pint64, Ret); - - // Make sure none of the instructions inserted were inttoptr/ptrtoint. - // The verifier will check this. - EXPECT_FALSE(verifyFunction(*F, &errs())); -} - -// Check that we can correctly identify the points at which the SCEV of the -// AddRec can be expanded. -TEST_F(ScalarEvolutionsTest, SCEVExpanderIsSafeToExpandAt) { - /* - * Create the following code: - * func(i64 addrspace(10)* %arg) - * top: - * br label %L.ph - * L.ph: - * br label %L - * L: - * %phi = phi i64 [i64 0, %L.ph], [ %add, %L2 ] - * %add = add i64 %phi2, 1 - * %cond = icmp slt i64 %add, 1000; then becomes 2000. - * br i1 %cond, label %post, label %L2 - * post: - * ret void - * - */ - - // Create a module with non-integral pointers in it's datalayout - Module NIM("nonintegral", Context); - std::string DataLayout = M.getDataLayoutStr(); - if (!DataLayout.empty()) - DataLayout += "-"; - DataLayout += "ni:10"; - NIM.setDataLayout(DataLayout); - - Type *T_int64 = Type::getInt64Ty(Context); - Type *T_pint64 = T_int64->getPointerTo(10); - - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), {T_pint64}, false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", NIM); - - BasicBlock *Top = BasicBlock::Create(Context, "top", F); - BasicBlock *LPh = BasicBlock::Create(Context, "L.ph", F); - BasicBlock *L = BasicBlock::Create(Context, "L", F); - BasicBlock *Post = BasicBlock::Create(Context, "post", F); - - IRBuilder<> Builder(Top); - Builder.CreateBr(LPh); - - Builder.SetInsertPoint(LPh); - Builder.CreateBr(L); - - Builder.SetInsertPoint(L); - PHINode *Phi = Builder.CreatePHI(T_int64, 2); - auto *Add = cast( - Builder.CreateAdd(Phi, ConstantInt::get(T_int64, 1), "add")); - auto *Limit = ConstantInt::get(T_int64, 1000); - auto *Cond = cast( - Builder.CreateICmp(ICmpInst::ICMP_SLT, Add, Limit, "cond")); - Builder.CreateCondBr(Cond, L, Post); - Phi->addIncoming(ConstantInt::get(T_int64, 0), LPh); - Phi->addIncoming(Add, L); - - Builder.SetInsertPoint(Post); - Builder.CreateRetVoid(); - - ScalarEvolution SE = buildSE(*F); - const SCEV *S = SE.getSCEV(Phi); - EXPECT_TRUE(isa(S)); - const SCEVAddRecExpr *AR = cast(S); - EXPECT_TRUE(AR->isAffine()); - EXPECT_FALSE(isSafeToExpandAt(AR, Top->getTerminator(), SE)); - EXPECT_FALSE(isSafeToExpandAt(AR, LPh->getTerminator(), SE)); - EXPECT_TRUE(isSafeToExpandAt(AR, L->getTerminator(), SE)); - EXPECT_TRUE(isSafeToExpandAt(AR, Post->getTerminator(), SE)); -} - -// Check that SCEV expander does not use the nuw instruction -// for expansion. -TEST_F(ScalarEvolutionsTest, SCEVExpanderNUW) { - /* - * Create the following code: - * func(i64 %a) - * entry: - * br false, label %exit, label %body - * body: - * %s1 = add i64 %a, -1 - * br label %exit - * exit: - * %s = add nuw i64 %a, -1 - * ret %s - */ - - // Create a module. - Module M("SCEVExpanderNUW", Context); - - Type *T_int64 = Type::getInt64Ty(Context); - - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), {T_int64}, false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); - Argument *Arg = &*F->arg_begin(); - ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); - - BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); - BasicBlock *Body = BasicBlock::Create(Context, "body", F); - BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); - - IRBuilder<> Builder(Entry); - ConstantInt *Cond = ConstantInt::get(Context, APInt(1, 0)); - Builder.CreateCondBr(Cond, Exit, Body); - - Builder.SetInsertPoint(Body); - auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); - Builder.CreateBr(Exit); - - Builder.SetInsertPoint(Exit); - auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); - S2->setHasNoUnsignedWrap(true); - auto *R = cast(Builder.CreateRetVoid()); - - ScalarEvolution SE = buildSE(*F); - const SCEV *S = SE.getSCEV(S1); - EXPECT_TRUE(isa(S)); - SCEVExpander Exp(SE, M.getDataLayout(), "expander"); - auto *I = cast(Exp.expandCodeFor(S, nullptr, R)); - EXPECT_FALSE(I->hasNoUnsignedWrap()); -} - -// Check that SCEV expander does not use the nsw instruction -// for expansion. -TEST_F(ScalarEvolutionsTest, SCEVExpanderNSW) { - /* - * Create the following code: - * func(i64 %a) - * entry: - * br false, label %exit, label %body - * body: - * %s1 = add i64 %a, -1 - * br label %exit - * exit: - * %s = add nsw i64 %a, -1 - * ret %s - */ - - // Create a module. - Module M("SCEVExpanderNSW", Context); - - Type *T_int64 = Type::getInt64Ty(Context); - - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), {T_int64}, false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); - Argument *Arg = &*F->arg_begin(); - ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); - - BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); - BasicBlock *Body = BasicBlock::Create(Context, "body", F); - BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); - - IRBuilder<> Builder(Entry); - ConstantInt *Cond = ConstantInt::get(Context, APInt(1, 0)); - Builder.CreateCondBr(Cond, Exit, Body); - - Builder.SetInsertPoint(Body); - auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); - Builder.CreateBr(Exit); - - Builder.SetInsertPoint(Exit); - auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); - S2->setHasNoSignedWrap(true); - auto *R = cast(Builder.CreateRetVoid()); - - ScalarEvolution SE = buildSE(*F); - const SCEV *S = SE.getSCEV(S1); - EXPECT_TRUE(isa(S)); - SCEVExpander Exp(SE, M.getDataLayout(), "expander"); - auto *I = cast(Exp.expandCodeFor(S, nullptr, R)); - EXPECT_FALSE(I->hasNoSignedWrap()); -} - -// Check that SCEV does not save the SCEV -> V -// mapping of SCEV differ from V in NUW flag. -TEST_F(ScalarEvolutionsTest, SCEVCacheNUW) { - /* - * Create the following code: - * func(i64 %a) - * entry: - * %s1 = add i64 %a, -1 - * %s2 = add nuw i64 %a, -1 - * br label %exit - * exit: - * ret %s - */ - - // Create a module. - Module M("SCEVCacheNUW", Context); - - Type *T_int64 = Type::getInt64Ty(Context); - - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), {T_int64}, false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); - Argument *Arg = &*F->arg_begin(); - ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); - - BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); - BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); - - IRBuilder<> Builder(Entry); - auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); - auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); - S2->setHasNoUnsignedWrap(true); - Builder.CreateBr(Exit); - - Builder.SetInsertPoint(Exit); - auto *R = cast(Builder.CreateRetVoid()); - - ScalarEvolution SE = buildSE(*F); - // Get S2 first to move it to cache. - const SCEV *SC2 = SE.getSCEV(S2); - EXPECT_TRUE(isa(SC2)); - // Now get S1. - const SCEV *SC1 = SE.getSCEV(S1); - EXPECT_TRUE(isa(SC1)); - // Expand for S1, it should use S1 not S2 in spite S2 - // first in the cache. - SCEVExpander Exp(SE, M.getDataLayout(), "expander"); - auto *I = cast(Exp.expandCodeFor(SC1, nullptr, R)); - EXPECT_FALSE(I->hasNoUnsignedWrap()); -} - -// Check that SCEV does not save the SCEV -> V -// mapping of SCEV differ from V in NSW flag. -TEST_F(ScalarEvolutionsTest, SCEVCacheNSW) { - /* - * Create the following code: - * func(i64 %a) - * entry: - * %s1 = add i64 %a, -1 - * %s2 = add nsw i64 %a, -1 - * br label %exit - * exit: - * ret %s - */ - - // Create a module. - Module M("SCEVCacheNUW", Context); - - Type *T_int64 = Type::getInt64Ty(Context); - - FunctionType *FTy = - FunctionType::get(Type::getVoidTy(Context), {T_int64}, false); - Function *F = Function::Create(FTy, Function::ExternalLinkage, "func", M); - Argument *Arg = &*F->arg_begin(); - ConstantInt *C = ConstantInt::get(Context, APInt(64, -1)); - - BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); - BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); - - IRBuilder<> Builder(Entry); - auto *S1 = cast(Builder.CreateAdd(Arg, C, "add")); - auto *S2 = cast(Builder.CreateAdd(Arg, C, "add")); - S2->setHasNoSignedWrap(true); - Builder.CreateBr(Exit); - - Builder.SetInsertPoint(Exit); - auto *R = cast(Builder.CreateRetVoid()); - - ScalarEvolution SE = buildSE(*F); - // Get S2 first to move it to cache. - const SCEV *SC2 = SE.getSCEV(S2); - EXPECT_TRUE(isa(SC2)); - // Now get S1. - const SCEV *SC1 = SE.getSCEV(S1); - EXPECT_TRUE(isa(SC1)); - // Expand for S1, it should use S1 not S2 in spite S2 - // first in the cache. - SCEVExpander Exp(SE, M.getDataLayout(), "expander"); - auto *I = cast(Exp.expandCodeFor(SC1, nullptr, R)); - EXPECT_FALSE(I->hasNoSignedWrap()); -} - -TEST_F(ScalarEvolutionsTest, SCEVExpandInsertCanonicalIV) { - LLVMContext C; - SMDiagnostic Err; - - // Expand the addrec produced by GetAddRec into a loop without a canonical IV. - // SCEVExpander will insert one. - auto TestNoCanonicalIV = - [&](std::function - GetAddRec) { - std::unique_ptr M = parseAssemblyString( - "define i32 @test(i32 %limit) { " - "entry: " - " br label %loop " - "loop: " - " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " - " %i.inc = add nsw i32 %i, 1 " - " %cont = icmp slt i32 %i.inc, %limit " - " br i1 %cont, label %loop, label %exit " - "exit: " - " ret i32 %i.inc " - "}", - Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - runWithSE( - *M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { - auto &I = GetInstByName(F, "i"); - auto *Loop = LI.getLoopFor(I.getParent()); - EXPECT_FALSE(Loop->getCanonicalInductionVariable()); - - auto *AR = GetAddRec(SE, Loop); - unsigned ExpectedCanonicalIVWidth = - SE.getTypeSizeInBits(AR->getType()); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *InsertAt = I.getNextNode(); - Exp.expandCodeFor(AR, nullptr, InsertAt); - PHINode *CanonicalIV = Loop->getCanonicalInductionVariable(); - unsigned CanonicalIVBitWidth = - cast(CanonicalIV->getType())->getBitWidth(); - EXPECT_EQ(CanonicalIVBitWidth, ExpectedCanonicalIVWidth); - }); - }; - - // Expand the addrec produced by GetAddRec into a loop with a canonical IV - // which is narrower than addrec type. - // SCEVExpander will insert a canonical IV of a wider type to expand the - // addrec. - auto TestNarrowCanonicalIV = [&](std::function - GetAddRec) { - std::unique_ptr M = parseAssemblyString( - "define i32 @test(i32 %limit) { " - "entry: " - " br label %loop " - "loop: " - " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " - " %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] " - " %i.inc = add nsw i32 %i, 1 " - " %canonical.iv.inc = add i8 %canonical.iv, 1 " - " %cont = icmp slt i32 %i.inc, %limit " - " br i1 %cont, label %loop, label %exit " - "exit: " - " ret i32 %i.inc " - "}", - Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { - auto &I = GetInstByName(F, "i"); - - auto *LoopHeaderBB = I.getParent(); - auto *Loop = LI.getLoopFor(LoopHeaderBB); - PHINode *CanonicalIV = Loop->getCanonicalInductionVariable(); - EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv")); - - auto *AR = GetAddRec(SE, Loop); - - unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType()); - unsigned CanonicalIVBitWidth = - cast(CanonicalIV->getType())->getBitWidth(); - EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *InsertAt = I.getNextNode(); - Exp.expandCodeFor(AR, nullptr, InsertAt); - - // Loop over all of the PHI nodes, looking for the new canonical indvar. - PHINode *NewCanonicalIV = nullptr; - for (BasicBlock::iterator i = LoopHeaderBB->begin(); isa(i); - ++i) { - PHINode *PN = cast(i); - if (PN == &I || PN == CanonicalIV) - continue; - // We expect that the only PHI added is the new canonical IV - EXPECT_FALSE(NewCanonicalIV); - NewCanonicalIV = PN; - } - - // Check that NewCanonicalIV is a canonical IV, i.e {0,+,1} - BasicBlock *Incoming = nullptr, *Backedge = nullptr; - EXPECT_TRUE(Loop->getIncomingAndBackEdge(Incoming, Backedge)); - auto *Start = NewCanonicalIV->getIncomingValueForBlock(Incoming); - EXPECT_TRUE(isa(Start)); - EXPECT_TRUE(dyn_cast(Start)->isZero()); - auto *Next = NewCanonicalIV->getIncomingValueForBlock(Backedge); - EXPECT_TRUE(isa(Next)); - auto *NextBinOp = dyn_cast(Next); - EXPECT_EQ(NextBinOp->getOpcode(), Instruction::Add); - EXPECT_EQ(NextBinOp->getOperand(0), NewCanonicalIV); - auto *Step = NextBinOp->getOperand(1); - EXPECT_TRUE(isa(Step)); - EXPECT_TRUE(dyn_cast(Step)->isOne()); - - unsigned NewCanonicalIVBitWidth = - cast(NewCanonicalIV->getType())->getBitWidth(); - EXPECT_EQ(NewCanonicalIVBitWidth, ExpectedCanonicalIVWidth); - }); - }; - - // Expand the addrec produced by GetAddRec into a loop with a canonical IV - // of addrec width. - // To expand the addrec SCEVExpander should use the existing canonical IV. - auto TestMatchingCanonicalIV = - [&](std::function GetAddRec, - unsigned ARBitWidth) { - auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth); - std::unique_ptr M = parseAssemblyString( - "define i32 @test(i32 %limit) { " - "entry: " - " br label %loop " - "loop: " - " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " - " %canonical.iv = phi " + - ARBitWidthTypeStr + - " [ 0, %entry ], [ %canonical.iv.inc, %loop ] " - " %i.inc = add nsw i32 %i, 1 " - " %canonical.iv.inc = add " + - ARBitWidthTypeStr + - " %canonical.iv, 1 " - " %cont = icmp slt i32 %i.inc, %limit " - " br i1 %cont, label %loop, label %exit " - "exit: " - " ret i32 %i.inc " - "}", - Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - runWithSE( - *M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { - auto &I = GetInstByName(F, "i"); - auto &CanonicalIV = GetInstByName(F, "canonical.iv"); - - auto *LoopHeaderBB = I.getParent(); - auto *Loop = LI.getLoopFor(LoopHeaderBB); - EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable()); - unsigned CanonicalIVBitWidth = - cast(CanonicalIV.getType())->getBitWidth(); - - auto *AR = GetAddRec(SE, Loop); - EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType())); - EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *InsertAt = I.getNextNode(); - Exp.expandCodeFor(AR, nullptr, InsertAt); - - // Loop over all of the PHI nodes, looking if a new canonical - // indvar was introduced. - PHINode *NewCanonicalIV = nullptr; - for (BasicBlock::iterator i = LoopHeaderBB->begin(); - isa(i); ++i) { - PHINode *PN = cast(i); - if (PN == &I || PN == &CanonicalIV) - continue; - NewCanonicalIV = PN; - } - EXPECT_FALSE(NewCanonicalIV); - }); - }; - - unsigned ARBitWidth = 16; - Type *ARType = IntegerType::get(C, ARBitWidth); - - // Expand {5,+,1} - auto GetAR2 = [&](ScalarEvolution &SE, Loop *L) -> const SCEV * { - return SE.getAddRecExpr(SE.getConstant(APInt(ARBitWidth, 5)), - SE.getOne(ARType), L, SCEV::FlagAnyWrap); - }; - TestNoCanonicalIV(GetAR2); - TestNarrowCanonicalIV(GetAR2); - TestMatchingCanonicalIV(GetAR2, ARBitWidth); -} - -TEST_F(ScalarEvolutionsTest, SCEVExpanderShlNSW) { - - auto checkOneCase = [this](std::string &&str) { - LLVMContext C; - SMDiagnostic Err; - std::unique_ptr M = parseAssemblyString(str, Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - Function *F = M->getFunction("f"); - ASSERT_NE(F, nullptr) << "Could not find function 'f'"; - - BasicBlock &Entry = F->getEntryBlock(); - LoadInst *Load = cast(&Entry.front()); - BinaryOperator *And = cast(*Load->user_begin()); - - ScalarEvolution SE = buildSE(*F); - const SCEV *AndSCEV = SE.getSCEV(And); - EXPECT_TRUE(isa(AndSCEV)); - EXPECT_TRUE(cast(AndSCEV)->hasNoSignedWrap()); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *I = cast(Exp.expandCodeFor(AndSCEV, nullptr, And)); - EXPECT_EQ(I->getOpcode(), Instruction::Shl); - EXPECT_FALSE(I->hasNoSignedWrap()); - }; - - checkOneCase("define void @f(i16* %arrayidx) { " - " %1 = load i16, i16* %arrayidx " - " %2 = and i16 %1, -32768 " - " ret void " - "} "); - - checkOneCase("define void @f(i8* %arrayidx) { " - " %1 = load i8, i8* %arrayidx " - " %2 = and i8 %1, -128 " - " ret void " - "} "); -} - -// Test expansion of nested addrecs in CanonicalMode. -// Expanding nested addrecs in canonical mode requiers a canonical IV of a -// type wider than the type of the addrec itself. Currently, SCEVExpander -// just falls back to literal mode for nested addrecs. -TEST_F(ScalarEvolutionsTest, SCEVExpandNonAffineAddRec) { - LLVMContext C; - SMDiagnostic Err; - - // Expand the addrec produced by GetAddRec into a loop without a canonical IV. - auto TestNoCanonicalIV = - [&](std::function - GetAddRec) { - std::unique_ptr M = parseAssemblyString( - "define i32 @test(i32 %limit) { " - "entry: " - " br label %loop " - "loop: " - " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " - " %i.inc = add nsw i32 %i, 1 " - " %cont = icmp slt i32 %i.inc, %limit " - " br i1 %cont, label %loop, label %exit " - "exit: " - " ret i32 %i.inc " - "}", - Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - runWithSE(*M, "test", - [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { - auto &I = GetInstByName(F, "i"); - auto *Loop = LI.getLoopFor(I.getParent()); - EXPECT_FALSE(Loop->getCanonicalInductionVariable()); - - auto *AR = GetAddRec(SE, Loop); - EXPECT_FALSE(AR->isAffine()); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *InsertAt = I.getNextNode(); - Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt); - auto *ExpandedAR = SE.getSCEV(V); - // Check that the expansion happened literally. - EXPECT_EQ(AR, ExpandedAR); - }); - }; - - // Expand the addrec produced by GetAddRec into a loop with a canonical IV - // which is narrower than addrec type. - auto TestNarrowCanonicalIV = [&](std::function - GetAddRec) { - std::unique_ptr M = parseAssemblyString( - "define i32 @test(i32 %limit) { " - "entry: " - " br label %loop " - "loop: " - " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " - " %canonical.iv = phi i8 [ 0, %entry ], [ %canonical.iv.inc, %loop ] " - " %i.inc = add nsw i32 %i, 1 " - " %canonical.iv.inc = add i8 %canonical.iv, 1 " - " %cont = icmp slt i32 %i.inc, %limit " - " br i1 %cont, label %loop, label %exit " - "exit: " - " ret i32 %i.inc " - "}", - Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - runWithSE(*M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { - auto &I = GetInstByName(F, "i"); - - auto *LoopHeaderBB = I.getParent(); - auto *Loop = LI.getLoopFor(LoopHeaderBB); - PHINode *CanonicalIV = Loop->getCanonicalInductionVariable(); - EXPECT_EQ(CanonicalIV, &GetInstByName(F, "canonical.iv")); - - auto *AR = GetAddRec(SE, Loop); - EXPECT_FALSE(AR->isAffine()); - - unsigned ExpectedCanonicalIVWidth = SE.getTypeSizeInBits(AR->getType()); - unsigned CanonicalIVBitWidth = - cast(CanonicalIV->getType())->getBitWidth(); - EXPECT_LT(CanonicalIVBitWidth, ExpectedCanonicalIVWidth); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *InsertAt = I.getNextNode(); - Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt); - auto *ExpandedAR = SE.getSCEV(V); - // Check that the expansion happened literally. - EXPECT_EQ(AR, ExpandedAR); - }); - }; - - // Expand the addrec produced by GetAddRec into a loop with a canonical IV - // of addrec width. - auto TestMatchingCanonicalIV = - [&](std::function - GetAddRec, - unsigned ARBitWidth) { - auto ARBitWidthTypeStr = "i" + std::to_string(ARBitWidth); - std::unique_ptr M = parseAssemblyString( - "define i32 @test(i32 %limit) { " - "entry: " - " br label %loop " - "loop: " - " %i = phi i32 [ 1, %entry ], [ %i.inc, %loop ] " - " %canonical.iv = phi " + - ARBitWidthTypeStr + - " [ 0, %entry ], [ %canonical.iv.inc, %loop ] " - " %i.inc = add nsw i32 %i, 1 " - " %canonical.iv.inc = add " + - ARBitWidthTypeStr + - " %canonical.iv, 1 " - " %cont = icmp slt i32 %i.inc, %limit " - " br i1 %cont, label %loop, label %exit " - "exit: " - " ret i32 %i.inc " - "}", - Err, C); - - assert(M && "Could not parse module?"); - assert(!verifyModule(*M) && "Must have been well formed!"); - - runWithSE( - *M, "test", [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) { - auto &I = GetInstByName(F, "i"); - auto &CanonicalIV = GetInstByName(F, "canonical.iv"); - - auto *LoopHeaderBB = I.getParent(); - auto *Loop = LI.getLoopFor(LoopHeaderBB); - EXPECT_EQ(&CanonicalIV, Loop->getCanonicalInductionVariable()); - unsigned CanonicalIVBitWidth = - cast(CanonicalIV.getType())->getBitWidth(); - - auto *AR = GetAddRec(SE, Loop); - EXPECT_FALSE(AR->isAffine()); - EXPECT_EQ(ARBitWidth, SE.getTypeSizeInBits(AR->getType())); - EXPECT_EQ(CanonicalIVBitWidth, ARBitWidth); - - SCEVExpander Exp(SE, M->getDataLayout(), "expander"); - auto *InsertAt = I.getNextNode(); - Value *V = Exp.expandCodeFor(AR, nullptr, InsertAt); - auto *ExpandedAR = SE.getSCEV(V); - // Check that the expansion happened literally. - EXPECT_EQ(AR, ExpandedAR); - }); - }; - - unsigned ARBitWidth = 16; - Type *ARType = IntegerType::get(C, ARBitWidth); - - // Expand {5,+,1,+,1} - auto GetAR3 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * { - SmallVector Ops = {SE.getConstant(APInt(ARBitWidth, 5)), - SE.getOne(ARType), SE.getOne(ARType)}; - return cast(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap)); - }; - TestNoCanonicalIV(GetAR3); - TestNarrowCanonicalIV(GetAR3); - TestMatchingCanonicalIV(GetAR3, ARBitWidth); - - // Expand {5,+,1,+,1,+,1} - auto GetAR4 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * { - SmallVector Ops = {SE.getConstant(APInt(ARBitWidth, 5)), - SE.getOne(ARType), SE.getOne(ARType), - SE.getOne(ARType)}; - return cast(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap)); - }; - TestNoCanonicalIV(GetAR4); - TestNarrowCanonicalIV(GetAR4); - TestMatchingCanonicalIV(GetAR4, ARBitWidth); - - // Expand {5,+,1,+,1,+,1,+,1} - auto GetAR5 = [&](ScalarEvolution &SE, Loop *L) -> const SCEVAddRecExpr * { - SmallVector Ops = {SE.getConstant(APInt(ARBitWidth, 5)), - SE.getOne(ARType), SE.getOne(ARType), - SE.getOne(ARType), SE.getOne(ARType)}; - return cast(SE.getAddRecExpr(Ops, L, SCEV::FlagAnyWrap)); - }; - TestNoCanonicalIV(GetAR5); - TestNarrowCanonicalIV(GetAR5); - TestMatchingCanonicalIV(GetAR5, ARBitWidth); -} - -} // end namespace llvm