2013-11-13 06:47:20 +08:00
|
|
|
//===---- Delinearization.cpp - MultiDimensional Index Delinearization ----===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2013-11-13 06:47:20 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This implements an analysis pass that tries to delinearize all GEP
|
|
|
|
// instructions in all loops using the SCEV analysis functionality. This pass is
|
|
|
|
// only used for testing purposes: if your pass needs delinearization, please
|
|
|
|
// use the on-demand SCEVAddRecExpr::delinearize() function.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-09-16 12:10:22 +08:00
|
|
|
#include "llvm/Analysis/Delinearization.h"
|
2014-01-07 19:48:04 +08:00
|
|
|
#include "llvm/Analysis/LoopInfo.h"
|
|
|
|
#include "llvm/Analysis/Passes.h"
|
|
|
|
#include "llvm/Analysis/ScalarEvolution.h"
|
2021-09-09 03:01:19 +08:00
|
|
|
#include "llvm/Analysis/ScalarEvolutionDivision.h"
|
2014-01-07 19:48:04 +08:00
|
|
|
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
2016-04-18 17:17:29 +08:00
|
|
|
#include "llvm/IR/Constants.h"
|
2013-11-13 06:47:20 +08:00
|
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
|
|
#include "llvm/IR/Function.h"
|
2014-03-04 18:30:26 +08:00
|
|
|
#include "llvm/IR/InstIterator.h"
|
2013-11-13 06:47:20 +08:00
|
|
|
#include "llvm/IR/Instructions.h"
|
|
|
|
#include "llvm/IR/LLVMContext.h"
|
2020-09-16 12:10:22 +08:00
|
|
|
#include "llvm/IR/PassManager.h"
|
2013-11-13 06:47:20 +08:00
|
|
|
#include "llvm/IR/Type.h"
|
Sink all InitializePasses.h includes
This file lists every pass in LLVM, and is included by Pass.h, which is
very popular. Every time we add, remove, or rename a pass in LLVM, it
caused lots of recompilation.
I found this fact by looking at this table, which is sorted by the
number of times a file was changed over the last 100,000 git commits
multiplied by the number of object files that depend on it in the
current checkout:
recompiles touches affected_files header
342380 95 3604 llvm/include/llvm/ADT/STLExtras.h
314730 234 1345 llvm/include/llvm/InitializePasses.h
307036 118 2602 llvm/include/llvm/ADT/APInt.h
213049 59 3611 llvm/include/llvm/Support/MathExtras.h
170422 47 3626 llvm/include/llvm/Support/Compiler.h
162225 45 3605 llvm/include/llvm/ADT/Optional.h
158319 63 2513 llvm/include/llvm/ADT/Triple.h
140322 39 3598 llvm/include/llvm/ADT/StringRef.h
137647 59 2333 llvm/include/llvm/Support/Error.h
131619 73 1803 llvm/include/llvm/Support/FileSystem.h
Before this change, touching InitializePasses.h would cause 1345 files
to recompile. After this change, touching it only causes 550 compiles in
an incremental rebuild.
Reviewers: bkramer, asbirlea, bollu, jdoerfert
Differential Revision: https://reviews.llvm.org/D70211
2019-11-14 05:15:01 +08:00
|
|
|
#include "llvm/InitializePasses.h"
|
2014-01-07 19:48:04 +08:00
|
|
|
#include "llvm/Pass.h"
|
2013-11-13 06:47:20 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
2014-04-22 10:48:03 +08:00
|
|
|
#define DL_NAME "delinearize"
|
|
|
|
#define DEBUG_TYPE DL_NAME
|
|
|
|
|
2021-09-09 03:01:19 +08:00
|
|
|
// Return true when S contains at least an undef value.
|
|
|
|
static inline bool containsUndefs(const SCEV *S) {
|
|
|
|
return SCEVExprContains(S, [](const SCEV *S) {
|
|
|
|
if (const auto *SU = dyn_cast<SCEVUnknown>(S))
|
|
|
|
return isa<UndefValue>(SU->getValue());
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Collect all steps of SCEV expressions.
|
|
|
|
struct SCEVCollectStrides {
|
|
|
|
ScalarEvolution &SE;
|
|
|
|
SmallVectorImpl<const SCEV *> &Strides;
|
|
|
|
|
|
|
|
SCEVCollectStrides(ScalarEvolution &SE, SmallVectorImpl<const SCEV *> &S)
|
|
|
|
: SE(SE), Strides(S) {}
|
|
|
|
|
|
|
|
bool follow(const SCEV *S) {
|
|
|
|
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(S))
|
|
|
|
Strides.push_back(AR->getStepRecurrence(SE));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDone() const { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Collect all SCEVUnknown and SCEVMulExpr expressions.
|
|
|
|
struct SCEVCollectTerms {
|
|
|
|
SmallVectorImpl<const SCEV *> &Terms;
|
|
|
|
|
|
|
|
SCEVCollectTerms(SmallVectorImpl<const SCEV *> &T) : Terms(T) {}
|
|
|
|
|
|
|
|
bool follow(const SCEV *S) {
|
|
|
|
if (isa<SCEVUnknown>(S) || isa<SCEVMulExpr>(S) ||
|
|
|
|
isa<SCEVSignExtendExpr>(S)) {
|
|
|
|
if (!containsUndefs(S))
|
|
|
|
Terms.push_back(S);
|
|
|
|
|
|
|
|
// Stop recursion: once we collected a term, do not walk its operands.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep looking.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDone() const { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check if a SCEV contains an AddRecExpr.
|
|
|
|
struct SCEVHasAddRec {
|
|
|
|
bool &ContainsAddRec;
|
|
|
|
|
|
|
|
SCEVHasAddRec(bool &ContainsAddRec) : ContainsAddRec(ContainsAddRec) {
|
|
|
|
ContainsAddRec = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool follow(const SCEV *S) {
|
|
|
|
if (isa<SCEVAddRecExpr>(S)) {
|
|
|
|
ContainsAddRec = true;
|
|
|
|
|
|
|
|
// Stop recursion: once we collected a term, do not walk its operands.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep looking.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDone() const { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Find factors that are multiplied with an expression that (possibly as a
|
|
|
|
// subexpression) contains an AddRecExpr. In the expression:
|
|
|
|
//
|
|
|
|
// 8 * (100 + %p * %q * (%a + {0, +, 1}_loop))
|
|
|
|
//
|
|
|
|
// "%p * %q" are factors multiplied by the expression "(%a + {0, +, 1}_loop)"
|
|
|
|
// that contains the AddRec {0, +, 1}_loop. %p * %q are likely to be array size
|
|
|
|
// parameters as they form a product with an induction variable.
|
|
|
|
//
|
|
|
|
// This collector expects all array size parameters to be in the same MulExpr.
|
|
|
|
// It might be necessary to later add support for collecting parameters that are
|
|
|
|
// spread over different nested MulExpr.
|
|
|
|
struct SCEVCollectAddRecMultiplies {
|
|
|
|
SmallVectorImpl<const SCEV *> &Terms;
|
|
|
|
ScalarEvolution &SE;
|
|
|
|
|
|
|
|
SCEVCollectAddRecMultiplies(SmallVectorImpl<const SCEV *> &T,
|
|
|
|
ScalarEvolution &SE)
|
|
|
|
: Terms(T), SE(SE) {}
|
|
|
|
|
|
|
|
bool follow(const SCEV *S) {
|
|
|
|
if (auto *Mul = dyn_cast<SCEVMulExpr>(S)) {
|
|
|
|
bool HasAddRec = false;
|
|
|
|
SmallVector<const SCEV *, 0> Operands;
|
|
|
|
for (auto Op : Mul->operands()) {
|
|
|
|
const SCEVUnknown *Unknown = dyn_cast<SCEVUnknown>(Op);
|
|
|
|
if (Unknown && !isa<CallInst>(Unknown->getValue())) {
|
|
|
|
Operands.push_back(Op);
|
|
|
|
} else if (Unknown) {
|
|
|
|
HasAddRec = true;
|
|
|
|
} else {
|
|
|
|
bool ContainsAddRec = false;
|
|
|
|
SCEVHasAddRec ContiansAddRec(ContainsAddRec);
|
|
|
|
visitAll(Op, ContiansAddRec);
|
|
|
|
HasAddRec |= ContainsAddRec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Operands.size() == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!HasAddRec)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Terms.push_back(SE.getMulExpr(Operands));
|
|
|
|
// Stop recursion: once we collected a term, do not walk its operands.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep looking.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDone() const { return false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
/// Find parametric terms in this SCEVAddRecExpr. We first for parameters in
|
|
|
|
/// two places:
|
|
|
|
/// 1) The strides of AddRec expressions.
|
|
|
|
/// 2) Unknowns that are multiplied with AddRec expressions.
|
|
|
|
void llvm::collectParametricTerms(ScalarEvolution &SE, const SCEV *Expr,
|
|
|
|
SmallVectorImpl<const SCEV *> &Terms) {
|
|
|
|
SmallVector<const SCEV *, 4> Strides;
|
|
|
|
SCEVCollectStrides StrideCollector(SE, Strides);
|
|
|
|
visitAll(Expr, StrideCollector);
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Strides:\n";
|
|
|
|
for (const SCEV *S : Strides)
|
|
|
|
dbgs() << *S << "\n";
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const SCEV *S : Strides) {
|
|
|
|
SCEVCollectTerms TermCollector(Terms);
|
|
|
|
visitAll(S, TermCollector);
|
|
|
|
}
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Terms:\n";
|
|
|
|
for (const SCEV *T : Terms)
|
|
|
|
dbgs() << *T << "\n";
|
|
|
|
});
|
|
|
|
|
|
|
|
SCEVCollectAddRecMultiplies MulCollector(Terms, SE);
|
|
|
|
visitAll(Expr, MulCollector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool findArrayDimensionsRec(ScalarEvolution &SE,
|
|
|
|
SmallVectorImpl<const SCEV *> &Terms,
|
|
|
|
SmallVectorImpl<const SCEV *> &Sizes) {
|
|
|
|
int Last = Terms.size() - 1;
|
|
|
|
const SCEV *Step = Terms[Last];
|
|
|
|
|
|
|
|
// End of recursion.
|
|
|
|
if (Last == 0) {
|
|
|
|
if (const SCEVMulExpr *M = dyn_cast<SCEVMulExpr>(Step)) {
|
|
|
|
SmallVector<const SCEV *, 2> Qs;
|
|
|
|
for (const SCEV *Op : M->operands())
|
|
|
|
if (!isa<SCEVConstant>(Op))
|
|
|
|
Qs.push_back(Op);
|
|
|
|
|
|
|
|
Step = SE.getMulExpr(Qs);
|
|
|
|
}
|
|
|
|
|
|
|
|
Sizes.push_back(Step);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const SCEV *&Term : Terms) {
|
|
|
|
// Normalize the terms before the next call to findArrayDimensionsRec.
|
|
|
|
const SCEV *Q, *R;
|
|
|
|
SCEVDivision::divide(SE, Term, Step, &Q, &R);
|
|
|
|
|
|
|
|
// Bail out when GCD does not evenly divide one of the terms.
|
|
|
|
if (!R->isZero())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Term = Q;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all SCEVConstants.
|
|
|
|
erase_if(Terms, [](const SCEV *E) { return isa<SCEVConstant>(E); });
|
|
|
|
|
|
|
|
if (Terms.size() > 0)
|
|
|
|
if (!findArrayDimensionsRec(SE, Terms, Sizes))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Sizes.push_back(Step);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true when one of the SCEVs of Terms contains a SCEVUnknown parameter.
|
|
|
|
static inline bool containsParameters(SmallVectorImpl<const SCEV *> &Terms) {
|
|
|
|
for (const SCEV *T : Terms)
|
|
|
|
if (SCEVExprContains(T, [](const SCEV *S) { return isa<SCEVUnknown>(S); }))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the number of product terms in S.
|
|
|
|
static inline int numberOfTerms(const SCEV *S) {
|
|
|
|
if (const SCEVMulExpr *Expr = dyn_cast<SCEVMulExpr>(S))
|
|
|
|
return Expr->getNumOperands();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const SCEV *removeConstantFactors(ScalarEvolution &SE, const SCEV *T) {
|
|
|
|
if (isa<SCEVConstant>(T))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (isa<SCEVUnknown>(T))
|
|
|
|
return T;
|
|
|
|
|
|
|
|
if (const SCEVMulExpr *M = dyn_cast<SCEVMulExpr>(T)) {
|
|
|
|
SmallVector<const SCEV *, 2> Factors;
|
|
|
|
for (const SCEV *Op : M->operands())
|
|
|
|
if (!isa<SCEVConstant>(Op))
|
|
|
|
Factors.push_back(Op);
|
|
|
|
|
|
|
|
return SE.getMulExpr(Factors);
|
|
|
|
}
|
|
|
|
|
|
|
|
return T;
|
|
|
|
}
|
|
|
|
|
|
|
|
void llvm::findArrayDimensions(ScalarEvolution &SE,
|
|
|
|
SmallVectorImpl<const SCEV *> &Terms,
|
|
|
|
SmallVectorImpl<const SCEV *> &Sizes,
|
|
|
|
const SCEV *ElementSize) {
|
|
|
|
if (Terms.size() < 1 || !ElementSize)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Early return when Terms do not contain parameters: we do not delinearize
|
|
|
|
// non parametric SCEVs.
|
|
|
|
if (!containsParameters(Terms))
|
|
|
|
return;
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Terms:\n";
|
|
|
|
for (const SCEV *T : Terms)
|
|
|
|
dbgs() << *T << "\n";
|
|
|
|
});
|
|
|
|
|
|
|
|
// Remove duplicates.
|
|
|
|
array_pod_sort(Terms.begin(), Terms.end());
|
|
|
|
Terms.erase(std::unique(Terms.begin(), Terms.end()), Terms.end());
|
|
|
|
|
|
|
|
// Put larger terms first.
|
|
|
|
llvm::sort(Terms, [](const SCEV *LHS, const SCEV *RHS) {
|
|
|
|
return numberOfTerms(LHS) > numberOfTerms(RHS);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Try to divide all terms by the element size. If term is not divisible by
|
|
|
|
// element size, proceed with the original term.
|
|
|
|
for (const SCEV *&Term : Terms) {
|
|
|
|
const SCEV *Q, *R;
|
|
|
|
SCEVDivision::divide(SE, Term, ElementSize, &Q, &R);
|
|
|
|
if (!Q->isZero())
|
|
|
|
Term = Q;
|
|
|
|
}
|
|
|
|
|
|
|
|
SmallVector<const SCEV *, 4> NewTerms;
|
|
|
|
|
|
|
|
// Remove constant factors.
|
|
|
|
for (const SCEV *T : Terms)
|
|
|
|
if (const SCEV *NewT = removeConstantFactors(SE, T))
|
|
|
|
NewTerms.push_back(NewT);
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Terms after sorting:\n";
|
|
|
|
for (const SCEV *T : NewTerms)
|
|
|
|
dbgs() << *T << "\n";
|
|
|
|
});
|
|
|
|
|
|
|
|
if (NewTerms.empty() || !findArrayDimensionsRec(SE, NewTerms, Sizes)) {
|
|
|
|
Sizes.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The last element to be pushed into Sizes is the size of an element.
|
|
|
|
Sizes.push_back(ElementSize);
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Sizes:\n";
|
|
|
|
for (const SCEV *S : Sizes)
|
|
|
|
dbgs() << *S << "\n";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
|
|
|
|
SmallVectorImpl<const SCEV *> &Subscripts,
|
|
|
|
SmallVectorImpl<const SCEV *> &Sizes) {
|
|
|
|
// Early exit in case this SCEV is not an affine multivariate function.
|
|
|
|
if (Sizes.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (auto *AR = dyn_cast<SCEVAddRecExpr>(Expr))
|
|
|
|
if (!AR->isAffine())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const SCEV *Res = Expr;
|
|
|
|
int Last = Sizes.size() - 1;
|
|
|
|
for (int i = Last; i >= 0; i--) {
|
|
|
|
const SCEV *Q, *R;
|
|
|
|
SCEVDivision::divide(SE, Res, Sizes[i], &Q, &R);
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Res: " << *Res << "\n";
|
|
|
|
dbgs() << "Sizes[i]: " << *Sizes[i] << "\n";
|
|
|
|
dbgs() << "Res divided by Sizes[i]:\n";
|
|
|
|
dbgs() << "Quotient: " << *Q << "\n";
|
|
|
|
dbgs() << "Remainder: " << *R << "\n";
|
|
|
|
});
|
|
|
|
|
|
|
|
Res = Q;
|
|
|
|
|
|
|
|
// Do not record the last subscript corresponding to the size of elements in
|
|
|
|
// the array.
|
|
|
|
if (i == Last) {
|
|
|
|
|
2021-09-09 04:36:39 +08:00
|
|
|
// Bail out if the byte offset is non-zero.
|
|
|
|
if (!R->isZero()) {
|
2021-09-09 03:01:19 +08:00
|
|
|
Subscripts.clear();
|
|
|
|
Sizes.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Record the access function for the current subscript.
|
|
|
|
Subscripts.push_back(R);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also push in last position the remainder of the last division: it will be
|
|
|
|
// the access function of the innermost dimension.
|
|
|
|
Subscripts.push_back(Res);
|
|
|
|
|
|
|
|
std::reverse(Subscripts.begin(), Subscripts.end());
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "Subscripts:\n";
|
|
|
|
for (const SCEV *S : Subscripts)
|
|
|
|
dbgs() << *S << "\n";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Splits the SCEV into two vectors of SCEVs representing the subscripts and
|
|
|
|
/// sizes of an array access. Returns the remainder of the delinearization that
|
|
|
|
/// is the offset start of the array. The SCEV->delinearize algorithm computes
|
|
|
|
/// the multiples of SCEV coefficients: that is a pattern matching of sub
|
|
|
|
/// expressions in the stride and base of a SCEV corresponding to the
|
|
|
|
/// computation of a GCD (greatest common divisor) of base and stride. When
|
|
|
|
/// SCEV->delinearize fails, it returns the SCEV unchanged.
|
|
|
|
///
|
|
|
|
/// For example: when analyzing the memory access A[i][j][k] in this loop nest
|
|
|
|
///
|
|
|
|
/// void foo(long n, long m, long o, double A[n][m][o]) {
|
|
|
|
///
|
|
|
|
/// for (long i = 0; i < n; i++)
|
|
|
|
/// for (long j = 0; j < m; j++)
|
|
|
|
/// for (long k = 0; k < o; k++)
|
|
|
|
/// A[i][j][k] = 1.0;
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// the delinearization input is the following AddRec SCEV:
|
|
|
|
///
|
|
|
|
/// AddRec: {{{%A,+,(8 * %m * %o)}<%for.i>,+,(8 * %o)}<%for.j>,+,8}<%for.k>
|
|
|
|
///
|
|
|
|
/// From this SCEV, we are able to say that the base offset of the access is %A
|
|
|
|
/// because it appears as an offset that does not divide any of the strides in
|
|
|
|
/// the loops:
|
|
|
|
///
|
|
|
|
/// CHECK: Base offset: %A
|
|
|
|
///
|
|
|
|
/// and then SCEV->delinearize determines the size of some of the dimensions of
|
|
|
|
/// the array as these are the multiples by which the strides are happening:
|
|
|
|
///
|
|
|
|
/// CHECK: ArrayDecl[UnknownSize][%m][%o] with elements of sizeof(double)
|
|
|
|
/// bytes.
|
|
|
|
///
|
|
|
|
/// Note that the outermost dimension remains of UnknownSize because there are
|
|
|
|
/// no strides that would help identifying the size of the last dimension: when
|
|
|
|
/// the array has been statically allocated, one could compute the size of that
|
|
|
|
/// dimension by dividing the overall size of the array by the size of the known
|
|
|
|
/// dimensions: %m * %o * 8.
|
|
|
|
///
|
|
|
|
/// Finally delinearize provides the access functions for the array reference
|
|
|
|
/// that does correspond to A[i][j][k] of the above C testcase:
|
|
|
|
///
|
|
|
|
/// CHECK: ArrayRef[{0,+,1}<%for.i>][{0,+,1}<%for.j>][{0,+,1}<%for.k>]
|
|
|
|
///
|
|
|
|
/// The testcases are checking the output of a function pass:
|
|
|
|
/// DelinearizationPass that walks through all loads and stores of a function
|
|
|
|
/// asking for the SCEV of the memory access with respect to all enclosing
|
|
|
|
/// loops, calling SCEV->delinearize on that and printing the results.
|
|
|
|
void llvm::delinearize(ScalarEvolution &SE, const SCEV *Expr,
|
|
|
|
SmallVectorImpl<const SCEV *> &Subscripts,
|
|
|
|
SmallVectorImpl<const SCEV *> &Sizes,
|
|
|
|
const SCEV *ElementSize) {
|
|
|
|
// First step: collect parametric terms.
|
|
|
|
SmallVector<const SCEV *, 4> Terms;
|
|
|
|
collectParametricTerms(SE, Expr, Terms);
|
|
|
|
|
|
|
|
if (Terms.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Second step: find subscript sizes.
|
|
|
|
findArrayDimensions(SE, Terms, Sizes, ElementSize);
|
|
|
|
|
|
|
|
if (Sizes.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Third step: compute the access functions for each subscript.
|
|
|
|
computeAccessFunctions(SE, Expr, Subscripts, Sizes);
|
|
|
|
|
|
|
|
if (Subscripts.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
LLVM_DEBUG({
|
|
|
|
dbgs() << "succeeded to delinearize " << *Expr << "\n";
|
|
|
|
dbgs() << "ArrayDecl[UnknownSize]";
|
|
|
|
for (const SCEV *S : Sizes)
|
|
|
|
dbgs() << "[" << *S << "]";
|
|
|
|
|
|
|
|
dbgs() << "\nArrayRef";
|
|
|
|
for (const SCEV *S : Subscripts)
|
|
|
|
dbgs() << "[" << *S << "]";
|
|
|
|
dbgs() << "\n";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-09 07:29:29 +08:00
|
|
|
bool llvm::getIndexExpressionsFromGEP(ScalarEvolution &SE,
|
|
|
|
const GetElementPtrInst *GEP,
|
|
|
|
SmallVectorImpl<const SCEV *> &Subscripts,
|
|
|
|
SmallVectorImpl<int> &Sizes) {
|
|
|
|
assert(Subscripts.empty() && Sizes.empty() &&
|
|
|
|
"Expected output lists to be empty on entry to this function.");
|
|
|
|
assert(GEP && "getIndexExpressionsFromGEP called with a null GEP");
|
|
|
|
Type *Ty = nullptr;
|
|
|
|
bool DroppedFirstDim = false;
|
|
|
|
for (unsigned i = 1; i < GEP->getNumOperands(); i++) {
|
|
|
|
const SCEV *Expr = SE.getSCEV(GEP->getOperand(i));
|
|
|
|
if (i == 1) {
|
|
|
|
Ty = GEP->getSourceElementType();
|
|
|
|
if (auto *Const = dyn_cast<SCEVConstant>(Expr))
|
|
|
|
if (Const->getValue()->isZero()) {
|
|
|
|
DroppedFirstDim = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Subscripts.push_back(Expr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto *ArrayTy = dyn_cast<ArrayType>(Ty);
|
|
|
|
if (!ArrayTy) {
|
|
|
|
Subscripts.clear();
|
|
|
|
Sizes.clear();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Subscripts.push_back(Expr);
|
|
|
|
if (!(DroppedFirstDim && i == 2))
|
|
|
|
Sizes.push_back(ArrayTy->getNumElements());
|
|
|
|
|
|
|
|
Ty = ArrayTy->getElementType();
|
|
|
|
}
|
|
|
|
return !Subscripts.empty();
|
|
|
|
}
|
|
|
|
|
2013-11-13 23:35:17 +08:00
|
|
|
namespace {
|
|
|
|
|
2013-11-13 06:47:20 +08:00
|
|
|
class Delinearization : public FunctionPass {
|
|
|
|
Delinearization(const Delinearization &); // do not implement
|
|
|
|
protected:
|
|
|
|
Function *F;
|
|
|
|
LoopInfo *LI;
|
|
|
|
ScalarEvolution *SE;
|
|
|
|
|
|
|
|
public:
|
|
|
|
static char ID; // Pass identification, replacement for typeid
|
|
|
|
|
|
|
|
Delinearization() : FunctionPass(ID) {
|
|
|
|
initializeDelinearizationPass(*PassRegistry::getPassRegistry());
|
|
|
|
}
|
2014-03-05 15:30:04 +08:00
|
|
|
bool runOnFunction(Function &F) override;
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
2014-04-15 12:59:12 +08:00
|
|
|
void print(raw_ostream &O, const Module *M = nullptr) const override;
|
2013-11-13 06:47:20 +08:00
|
|
|
};
|
|
|
|
|
2020-09-16 12:10:22 +08:00
|
|
|
void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,
|
|
|
|
ScalarEvolution *SE) {
|
2013-11-13 06:47:20 +08:00
|
|
|
O << "Delinearization on function " << F->getName() << ":\n";
|
2021-02-07 03:17:09 +08:00
|
|
|
for (Instruction &Inst : instructions(F)) {
|
2013-11-14 06:37:58 +08:00
|
|
|
// Only analyze loads and stores.
|
2021-02-07 03:17:09 +08:00
|
|
|
if (!isa<StoreInst>(&Inst) && !isa<LoadInst>(&Inst) &&
|
|
|
|
!isa<GetElementPtrInst>(&Inst))
|
2013-11-13 06:47:20 +08:00
|
|
|
continue;
|
|
|
|
|
2021-02-07 03:17:09 +08:00
|
|
|
const BasicBlock *BB = Inst.getParent();
|
2013-11-13 06:47:20 +08:00
|
|
|
// Delinearize the memory access as analyzed in all the surrounding loops.
|
|
|
|
// Do not analyze memory accesses outside loops.
|
2014-04-15 12:59:12 +08:00
|
|
|
for (Loop *L = LI->getLoopFor(BB); L != nullptr; L = L->getParentLoop()) {
|
2021-02-07 03:17:09 +08:00
|
|
|
const SCEV *AccessFn = SE->getSCEVAtScope(getPointerOperand(&Inst), L);
|
2014-05-28 06:41:51 +08:00
|
|
|
|
|
|
|
const SCEVUnknown *BasePointer =
|
|
|
|
dyn_cast<SCEVUnknown>(SE->getPointerBase(AccessFn));
|
|
|
|
// Do not delinearize if we cannot find the base pointer.
|
|
|
|
if (!BasePointer)
|
|
|
|
break;
|
|
|
|
AccessFn = SE->getMinusSCEV(AccessFn, BasePointer);
|
|
|
|
|
2014-04-09 15:53:49 +08:00
|
|
|
O << "\n";
|
2021-02-07 03:17:09 +08:00
|
|
|
O << "Inst:" << Inst << "\n";
|
2014-04-09 15:53:49 +08:00
|
|
|
O << "In Loop with Header: " << L->getHeader()->getName() << "\n";
|
2015-10-12 16:02:00 +08:00
|
|
|
O << "AccessFunction: " << *AccessFn << "\n";
|
2013-11-13 06:47:20 +08:00
|
|
|
|
|
|
|
SmallVector<const SCEV *, 3> Subscripts, Sizes;
|
2021-09-09 03:01:19 +08:00
|
|
|
delinearize(*SE, AccessFn, Subscripts, Sizes, SE->getElementSize(&Inst));
|
2014-05-28 06:41:45 +08:00
|
|
|
if (Subscripts.size() == 0 || Sizes.size() == 0 ||
|
2014-05-08 02:01:20 +08:00
|
|
|
Subscripts.size() != Sizes.size()) {
|
2013-11-13 06:47:20 +08:00
|
|
|
O << "failed to delinearize\n";
|
|
|
|
continue;
|
|
|
|
}
|
2014-05-28 06:41:51 +08:00
|
|
|
|
|
|
|
O << "Base offset: " << *BasePointer << "\n";
|
2013-11-13 06:47:20 +08:00
|
|
|
O << "ArrayDecl[UnknownSize]";
|
2014-05-08 02:01:20 +08:00
|
|
|
int Size = Subscripts.size();
|
2013-11-13 06:47:20 +08:00
|
|
|
for (int i = 0; i < Size - 1; i++)
|
|
|
|
O << "[" << *Sizes[i] << "]";
|
|
|
|
O << " with elements of " << *Sizes[Size - 1] << " bytes.\n";
|
|
|
|
|
|
|
|
O << "ArrayRef";
|
|
|
|
for (int i = 0; i < Size; i++)
|
|
|
|
O << "[" << *Subscripts[i] << "]";
|
|
|
|
O << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-16 12:10:22 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
void Delinearization::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
|
|
AU.setPreservesAll();
|
|
|
|
AU.addRequired<LoopInfoWrapperPass>();
|
|
|
|
AU.addRequired<ScalarEvolutionWrapperPass>();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Delinearization::runOnFunction(Function &F) {
|
|
|
|
this->F = &F;
|
|
|
|
SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
|
|
|
|
LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Delinearization::print(raw_ostream &O, const Module *) const {
|
|
|
|
printDelinearization(O, F, LI, SE);
|
|
|
|
}
|
|
|
|
|
2013-11-13 06:47:20 +08:00
|
|
|
char Delinearization::ID = 0;
|
|
|
|
static const char delinearization_name[] = "Delinearization";
|
|
|
|
INITIALIZE_PASS_BEGIN(Delinearization, DL_NAME, delinearization_name, true,
|
|
|
|
true)
|
2015-01-17 22:16:18 +08:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
|
2013-11-13 06:47:20 +08:00
|
|
|
INITIALIZE_PASS_END(Delinearization, DL_NAME, delinearization_name, true, true)
|
|
|
|
|
|
|
|
FunctionPass *llvm::createDelinearizationPass() { return new Delinearization; }
|
2020-09-16 12:10:22 +08:00
|
|
|
|
|
|
|
DelinearizationPrinterPass::DelinearizationPrinterPass(raw_ostream &OS)
|
|
|
|
: OS(OS) {}
|
|
|
|
PreservedAnalyses DelinearizationPrinterPass::run(Function &F,
|
|
|
|
FunctionAnalysisManager &AM) {
|
|
|
|
printDelinearization(OS, &F, &AM.getResult<LoopAnalysis>(F),
|
|
|
|
&AM.getResult<ScalarEvolutionAnalysis>(F));
|
|
|
|
return PreservedAnalyses::all();
|
|
|
|
}
|