Path: blob/main/contrib/llvm-project/llvm/lib/Analysis/Delinearization.cpp
35233 views
//===---- Delinearization.cpp - MultiDimensional Index Delinearization ----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This implements an analysis pass that tries to delinearize all GEP9// instructions in all loops using the SCEV analysis functionality. This pass is10// only used for testing purposes: if your pass needs delinearization, please11// use the on-demand SCEVAddRecExpr::delinearize() function.12//13//===----------------------------------------------------------------------===//1415#include "llvm/Analysis/Delinearization.h"16#include "llvm/Analysis/LoopInfo.h"17#include "llvm/Analysis/Passes.h"18#include "llvm/Analysis/ScalarEvolution.h"19#include "llvm/Analysis/ScalarEvolutionDivision.h"20#include "llvm/Analysis/ScalarEvolutionExpressions.h"21#include "llvm/IR/Constants.h"22#include "llvm/IR/DerivedTypes.h"23#include "llvm/IR/Function.h"24#include "llvm/IR/InstIterator.h"25#include "llvm/IR/Instructions.h"26#include "llvm/IR/PassManager.h"27#include "llvm/Support/Debug.h"28#include "llvm/Support/raw_ostream.h"2930using namespace llvm;3132#define DL_NAME "delinearize"33#define DEBUG_TYPE DL_NAME3435// Return true when S contains at least an undef value.36static inline bool containsUndefs(const SCEV *S) {37return SCEVExprContains(S, [](const SCEV *S) {38if (const auto *SU = dyn_cast<SCEVUnknown>(S))39return isa<UndefValue>(SU->getValue());40return false;41});42}4344namespace {4546// Collect all steps of SCEV expressions.47struct SCEVCollectStrides {48ScalarEvolution &SE;49SmallVectorImpl<const SCEV *> &Strides;5051SCEVCollectStrides(ScalarEvolution &SE, SmallVectorImpl<const SCEV *> &S)52: SE(SE), Strides(S) {}5354bool follow(const SCEV *S) {55if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(S))56Strides.push_back(AR->getStepRecurrence(SE));57return true;58}5960bool isDone() const { return false; }61};6263// Collect all SCEVUnknown and SCEVMulExpr expressions.64struct SCEVCollectTerms {65SmallVectorImpl<const SCEV *> &Terms;6667SCEVCollectTerms(SmallVectorImpl<const SCEV *> &T) : Terms(T) {}6869bool follow(const SCEV *S) {70if (isa<SCEVUnknown>(S) || isa<SCEVMulExpr>(S) ||71isa<SCEVSignExtendExpr>(S)) {72if (!containsUndefs(S))73Terms.push_back(S);7475// Stop recursion: once we collected a term, do not walk its operands.76return false;77}7879// Keep looking.80return true;81}8283bool isDone() const { return false; }84};8586// Check if a SCEV contains an AddRecExpr.87struct SCEVHasAddRec {88bool &ContainsAddRec;8990SCEVHasAddRec(bool &ContainsAddRec) : ContainsAddRec(ContainsAddRec) {91ContainsAddRec = false;92}9394bool follow(const SCEV *S) {95if (isa<SCEVAddRecExpr>(S)) {96ContainsAddRec = true;9798// Stop recursion: once we collected a term, do not walk its operands.99return false;100}101102// Keep looking.103return true;104}105106bool isDone() const { return false; }107};108109// Find factors that are multiplied with an expression that (possibly as a110// subexpression) contains an AddRecExpr. In the expression:111//112// 8 * (100 + %p * %q * (%a + {0, +, 1}_loop))113//114// "%p * %q" are factors multiplied by the expression "(%a + {0, +, 1}_loop)"115// that contains the AddRec {0, +, 1}_loop. %p * %q are likely to be array size116// parameters as they form a product with an induction variable.117//118// This collector expects all array size parameters to be in the same MulExpr.119// It might be necessary to later add support for collecting parameters that are120// spread over different nested MulExpr.121struct SCEVCollectAddRecMultiplies {122SmallVectorImpl<const SCEV *> &Terms;123ScalarEvolution &SE;124125SCEVCollectAddRecMultiplies(SmallVectorImpl<const SCEV *> &T,126ScalarEvolution &SE)127: Terms(T), SE(SE) {}128129bool follow(const SCEV *S) {130if (auto *Mul = dyn_cast<SCEVMulExpr>(S)) {131bool HasAddRec = false;132SmallVector<const SCEV *, 0> Operands;133for (const auto *Op : Mul->operands()) {134const SCEVUnknown *Unknown = dyn_cast<SCEVUnknown>(Op);135if (Unknown && !isa<CallInst>(Unknown->getValue())) {136Operands.push_back(Op);137} else if (Unknown) {138HasAddRec = true;139} else {140bool ContainsAddRec = false;141SCEVHasAddRec ContiansAddRec(ContainsAddRec);142visitAll(Op, ContiansAddRec);143HasAddRec |= ContainsAddRec;144}145}146if (Operands.size() == 0)147return true;148149if (!HasAddRec)150return false;151152Terms.push_back(SE.getMulExpr(Operands));153// Stop recursion: once we collected a term, do not walk its operands.154return false;155}156157// Keep looking.158return true;159}160161bool isDone() const { return false; }162};163164} // end anonymous namespace165166/// Find parametric terms in this SCEVAddRecExpr. We first for parameters in167/// two places:168/// 1) The strides of AddRec expressions.169/// 2) Unknowns that are multiplied with AddRec expressions.170void llvm::collectParametricTerms(ScalarEvolution &SE, const SCEV *Expr,171SmallVectorImpl<const SCEV *> &Terms) {172SmallVector<const SCEV *, 4> Strides;173SCEVCollectStrides StrideCollector(SE, Strides);174visitAll(Expr, StrideCollector);175176LLVM_DEBUG({177dbgs() << "Strides:\n";178for (const SCEV *S : Strides)179dbgs() << *S << "\n";180});181182for (const SCEV *S : Strides) {183SCEVCollectTerms TermCollector(Terms);184visitAll(S, TermCollector);185}186187LLVM_DEBUG({188dbgs() << "Terms:\n";189for (const SCEV *T : Terms)190dbgs() << *T << "\n";191});192193SCEVCollectAddRecMultiplies MulCollector(Terms, SE);194visitAll(Expr, MulCollector);195}196197static bool findArrayDimensionsRec(ScalarEvolution &SE,198SmallVectorImpl<const SCEV *> &Terms,199SmallVectorImpl<const SCEV *> &Sizes) {200int Last = Terms.size() - 1;201const SCEV *Step = Terms[Last];202203// End of recursion.204if (Last == 0) {205if (const SCEVMulExpr *M = dyn_cast<SCEVMulExpr>(Step)) {206SmallVector<const SCEV *, 2> Qs;207for (const SCEV *Op : M->operands())208if (!isa<SCEVConstant>(Op))209Qs.push_back(Op);210211Step = SE.getMulExpr(Qs);212}213214Sizes.push_back(Step);215return true;216}217218for (const SCEV *&Term : Terms) {219// Normalize the terms before the next call to findArrayDimensionsRec.220const SCEV *Q, *R;221SCEVDivision::divide(SE, Term, Step, &Q, &R);222223// Bail out when GCD does not evenly divide one of the terms.224if (!R->isZero())225return false;226227Term = Q;228}229230// Remove all SCEVConstants.231erase_if(Terms, [](const SCEV *E) { return isa<SCEVConstant>(E); });232233if (Terms.size() > 0)234if (!findArrayDimensionsRec(SE, Terms, Sizes))235return false;236237Sizes.push_back(Step);238return true;239}240241// Returns true when one of the SCEVs of Terms contains a SCEVUnknown parameter.242static inline bool containsParameters(SmallVectorImpl<const SCEV *> &Terms) {243for (const SCEV *T : Terms)244if (SCEVExprContains(T, [](const SCEV *S) { return isa<SCEVUnknown>(S); }))245return true;246247return false;248}249250// Return the number of product terms in S.251static inline int numberOfTerms(const SCEV *S) {252if (const SCEVMulExpr *Expr = dyn_cast<SCEVMulExpr>(S))253return Expr->getNumOperands();254return 1;255}256257static const SCEV *removeConstantFactors(ScalarEvolution &SE, const SCEV *T) {258if (isa<SCEVConstant>(T))259return nullptr;260261if (isa<SCEVUnknown>(T))262return T;263264if (const SCEVMulExpr *M = dyn_cast<SCEVMulExpr>(T)) {265SmallVector<const SCEV *, 2> Factors;266for (const SCEV *Op : M->operands())267if (!isa<SCEVConstant>(Op))268Factors.push_back(Op);269270return SE.getMulExpr(Factors);271}272273return T;274}275276void llvm::findArrayDimensions(ScalarEvolution &SE,277SmallVectorImpl<const SCEV *> &Terms,278SmallVectorImpl<const SCEV *> &Sizes,279const SCEV *ElementSize) {280if (Terms.size() < 1 || !ElementSize)281return;282283// Early return when Terms do not contain parameters: we do not delinearize284// non parametric SCEVs.285if (!containsParameters(Terms))286return;287288LLVM_DEBUG({289dbgs() << "Terms:\n";290for (const SCEV *T : Terms)291dbgs() << *T << "\n";292});293294// Remove duplicates.295array_pod_sort(Terms.begin(), Terms.end());296Terms.erase(llvm::unique(Terms), Terms.end());297298// Put larger terms first.299llvm::sort(Terms, [](const SCEV *LHS, const SCEV *RHS) {300return numberOfTerms(LHS) > numberOfTerms(RHS);301});302303// Try to divide all terms by the element size. If term is not divisible by304// element size, proceed with the original term.305for (const SCEV *&Term : Terms) {306const SCEV *Q, *R;307SCEVDivision::divide(SE, Term, ElementSize, &Q, &R);308if (!Q->isZero())309Term = Q;310}311312SmallVector<const SCEV *, 4> NewTerms;313314// Remove constant factors.315for (const SCEV *T : Terms)316if (const SCEV *NewT = removeConstantFactors(SE, T))317NewTerms.push_back(NewT);318319LLVM_DEBUG({320dbgs() << "Terms after sorting:\n";321for (const SCEV *T : NewTerms)322dbgs() << *T << "\n";323});324325if (NewTerms.empty() || !findArrayDimensionsRec(SE, NewTerms, Sizes)) {326Sizes.clear();327return;328}329330// The last element to be pushed into Sizes is the size of an element.331Sizes.push_back(ElementSize);332333LLVM_DEBUG({334dbgs() << "Sizes:\n";335for (const SCEV *S : Sizes)336dbgs() << *S << "\n";337});338}339340void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,341SmallVectorImpl<const SCEV *> &Subscripts,342SmallVectorImpl<const SCEV *> &Sizes) {343// Early exit in case this SCEV is not an affine multivariate function.344if (Sizes.empty())345return;346347if (auto *AR = dyn_cast<SCEVAddRecExpr>(Expr))348if (!AR->isAffine())349return;350351const SCEV *Res = Expr;352int Last = Sizes.size() - 1;353for (int i = Last; i >= 0; i--) {354const SCEV *Q, *R;355SCEVDivision::divide(SE, Res, Sizes[i], &Q, &R);356357LLVM_DEBUG({358dbgs() << "Res: " << *Res << "\n";359dbgs() << "Sizes[i]: " << *Sizes[i] << "\n";360dbgs() << "Res divided by Sizes[i]:\n";361dbgs() << "Quotient: " << *Q << "\n";362dbgs() << "Remainder: " << *R << "\n";363});364365Res = Q;366367// Do not record the last subscript corresponding to the size of elements in368// the array.369if (i == Last) {370371// Bail out if the byte offset is non-zero.372if (!R->isZero()) {373Subscripts.clear();374Sizes.clear();375return;376}377378continue;379}380381// Record the access function for the current subscript.382Subscripts.push_back(R);383}384385// Also push in last position the remainder of the last division: it will be386// the access function of the innermost dimension.387Subscripts.push_back(Res);388389std::reverse(Subscripts.begin(), Subscripts.end());390391LLVM_DEBUG({392dbgs() << "Subscripts:\n";393for (const SCEV *S : Subscripts)394dbgs() << *S << "\n";395});396}397398/// Splits the SCEV into two vectors of SCEVs representing the subscripts and399/// sizes of an array access. Returns the remainder of the delinearization that400/// is the offset start of the array. The SCEV->delinearize algorithm computes401/// the multiples of SCEV coefficients: that is a pattern matching of sub402/// expressions in the stride and base of a SCEV corresponding to the403/// computation of a GCD (greatest common divisor) of base and stride. When404/// SCEV->delinearize fails, it returns the SCEV unchanged.405///406/// For example: when analyzing the memory access A[i][j][k] in this loop nest407///408/// void foo(long n, long m, long o, double A[n][m][o]) {409///410/// for (long i = 0; i < n; i++)411/// for (long j = 0; j < m; j++)412/// for (long k = 0; k < o; k++)413/// A[i][j][k] = 1.0;414/// }415///416/// the delinearization input is the following AddRec SCEV:417///418/// AddRec: {{{%A,+,(8 * %m * %o)}<%for.i>,+,(8 * %o)}<%for.j>,+,8}<%for.k>419///420/// From this SCEV, we are able to say that the base offset of the access is %A421/// because it appears as an offset that does not divide any of the strides in422/// the loops:423///424/// CHECK: Base offset: %A425///426/// and then SCEV->delinearize determines the size of some of the dimensions of427/// the array as these are the multiples by which the strides are happening:428///429/// CHECK: ArrayDecl[UnknownSize][%m][%o] with elements of sizeof(double)430/// bytes.431///432/// Note that the outermost dimension remains of UnknownSize because there are433/// no strides that would help identifying the size of the last dimension: when434/// the array has been statically allocated, one could compute the size of that435/// dimension by dividing the overall size of the array by the size of the known436/// dimensions: %m * %o * 8.437///438/// Finally delinearize provides the access functions for the array reference439/// that does correspond to A[i][j][k] of the above C testcase:440///441/// CHECK: ArrayRef[{0,+,1}<%for.i>][{0,+,1}<%for.j>][{0,+,1}<%for.k>]442///443/// The testcases are checking the output of a function pass:444/// DelinearizationPass that walks through all loads and stores of a function445/// asking for the SCEV of the memory access with respect to all enclosing446/// loops, calling SCEV->delinearize on that and printing the results.447void llvm::delinearize(ScalarEvolution &SE, const SCEV *Expr,448SmallVectorImpl<const SCEV *> &Subscripts,449SmallVectorImpl<const SCEV *> &Sizes,450const SCEV *ElementSize) {451// First step: collect parametric terms.452SmallVector<const SCEV *, 4> Terms;453collectParametricTerms(SE, Expr, Terms);454455if (Terms.empty())456return;457458// Second step: find subscript sizes.459findArrayDimensions(SE, Terms, Sizes, ElementSize);460461if (Sizes.empty())462return;463464// Third step: compute the access functions for each subscript.465computeAccessFunctions(SE, Expr, Subscripts, Sizes);466467if (Subscripts.empty())468return;469470LLVM_DEBUG({471dbgs() << "succeeded to delinearize " << *Expr << "\n";472dbgs() << "ArrayDecl[UnknownSize]";473for (const SCEV *S : Sizes)474dbgs() << "[" << *S << "]";475476dbgs() << "\nArrayRef";477for (const SCEV *S : Subscripts)478dbgs() << "[" << *S << "]";479dbgs() << "\n";480});481}482483bool llvm::getIndexExpressionsFromGEP(ScalarEvolution &SE,484const GetElementPtrInst *GEP,485SmallVectorImpl<const SCEV *> &Subscripts,486SmallVectorImpl<int> &Sizes) {487assert(Subscripts.empty() && Sizes.empty() &&488"Expected output lists to be empty on entry to this function.");489assert(GEP && "getIndexExpressionsFromGEP called with a null GEP");490Type *Ty = nullptr;491bool DroppedFirstDim = false;492for (unsigned i = 1; i < GEP->getNumOperands(); i++) {493const SCEV *Expr = SE.getSCEV(GEP->getOperand(i));494if (i == 1) {495Ty = GEP->getSourceElementType();496if (auto *Const = dyn_cast<SCEVConstant>(Expr))497if (Const->getValue()->isZero()) {498DroppedFirstDim = true;499continue;500}501Subscripts.push_back(Expr);502continue;503}504505auto *ArrayTy = dyn_cast<ArrayType>(Ty);506if (!ArrayTy) {507Subscripts.clear();508Sizes.clear();509return false;510}511512Subscripts.push_back(Expr);513if (!(DroppedFirstDim && i == 2))514Sizes.push_back(ArrayTy->getNumElements());515516Ty = ArrayTy->getElementType();517}518return !Subscripts.empty();519}520521bool llvm::tryDelinearizeFixedSizeImpl(522ScalarEvolution *SE, Instruction *Inst, const SCEV *AccessFn,523SmallVectorImpl<const SCEV *> &Subscripts, SmallVectorImpl<int> &Sizes) {524Value *SrcPtr = getLoadStorePointerOperand(Inst);525526// Check the simple case where the array dimensions are fixed size.527auto *SrcGEP = dyn_cast<GetElementPtrInst>(SrcPtr);528if (!SrcGEP)529return false;530531getIndexExpressionsFromGEP(*SE, SrcGEP, Subscripts, Sizes);532533// Check that the two size arrays are non-empty and equal in length and534// value.535// TODO: it would be better to let the caller to clear Subscripts, similar536// to how we handle Sizes.537if (Sizes.empty() || Subscripts.size() <= 1) {538Subscripts.clear();539return false;540}541542// Check that for identical base pointers we do not miss index offsets543// that have been added before this GEP is applied.544Value *SrcBasePtr = SrcGEP->getOperand(0)->stripPointerCasts();545const SCEVUnknown *SrcBase =546dyn_cast<SCEVUnknown>(SE->getPointerBase(AccessFn));547if (!SrcBase || SrcBasePtr != SrcBase->getValue()) {548Subscripts.clear();549return false;550}551552assert(Subscripts.size() == Sizes.size() + 1 &&553"Expected equal number of entries in the list of size and "554"subscript.");555556return true;557}558559namespace {560561void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,562ScalarEvolution *SE) {563O << "Delinearization on function " << F->getName() << ":\n";564for (Instruction &Inst : instructions(F)) {565// Only analyze loads and stores.566if (!isa<StoreInst>(&Inst) && !isa<LoadInst>(&Inst) &&567!isa<GetElementPtrInst>(&Inst))568continue;569570const BasicBlock *BB = Inst.getParent();571// Delinearize the memory access as analyzed in all the surrounding loops.572// Do not analyze memory accesses outside loops.573for (Loop *L = LI->getLoopFor(BB); L != nullptr; L = L->getParentLoop()) {574const SCEV *AccessFn = SE->getSCEVAtScope(getPointerOperand(&Inst), L);575576const SCEVUnknown *BasePointer =577dyn_cast<SCEVUnknown>(SE->getPointerBase(AccessFn));578// Do not delinearize if we cannot find the base pointer.579if (!BasePointer)580break;581AccessFn = SE->getMinusSCEV(AccessFn, BasePointer);582583O << "\n";584O << "Inst:" << Inst << "\n";585O << "In Loop with Header: " << L->getHeader()->getName() << "\n";586O << "AccessFunction: " << *AccessFn << "\n";587588SmallVector<const SCEV *, 3> Subscripts, Sizes;589delinearize(*SE, AccessFn, Subscripts, Sizes, SE->getElementSize(&Inst));590if (Subscripts.size() == 0 || Sizes.size() == 0 ||591Subscripts.size() != Sizes.size()) {592O << "failed to delinearize\n";593continue;594}595596O << "Base offset: " << *BasePointer << "\n";597O << "ArrayDecl[UnknownSize]";598int Size = Subscripts.size();599for (int i = 0; i < Size - 1; i++)600O << "[" << *Sizes[i] << "]";601O << " with elements of " << *Sizes[Size - 1] << " bytes.\n";602603O << "ArrayRef";604for (int i = 0; i < Size; i++)605O << "[" << *Subscripts[i] << "]";606O << "\n";607}608}609}610611} // end anonymous namespace612613DelinearizationPrinterPass::DelinearizationPrinterPass(raw_ostream &OS)614: OS(OS) {}615PreservedAnalyses DelinearizationPrinterPass::run(Function &F,616FunctionAnalysisManager &AM) {617printDelinearization(OS, &F, &AM.getResult<LoopAnalysis>(F),618&AM.getResult<ScalarEvolutionAnalysis>(F));619return PreservedAnalyses::all();620}621622623