add more comments around the delinearization of arrays

llvm-svn: 194612
This commit is contained in:
Sebastian Pop 2013-11-13 22:37:58 +00:00
parent 7244bee1a8
commit 7ee147246f
3 changed files with 88 additions and 17 deletions

View File

@ -83,6 +83,8 @@ void Delinearization::print(raw_ostream &O, const Module *) const {
O << "Delinearization on function " << F->getName() << ":\n"; O << "Delinearization on function " << F->getName() << ":\n";
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
Instruction *Inst = &(*I); Instruction *Inst = &(*I);
// Only analyze loads and stores.
if (!isa<StoreInst>(Inst) && !isa<LoadInst>(Inst) && if (!isa<StoreInst>(Inst) && !isa<LoadInst>(Inst) &&
!isa<GetElementPtrInst>(Inst)) !isa<GetElementPtrInst>(Inst))
continue; continue;
@ -93,6 +95,8 @@ void Delinearization::print(raw_ostream &O, const Module *) const {
for (Loop *L = LI->getLoopFor(BB); L != NULL; L = L->getParentLoop()) { for (Loop *L = LI->getLoopFor(BB); L != NULL; L = L->getParentLoop()) {
const SCEV *AccessFn = SE->getSCEVAtScope(getPointerOperand(*Inst), L); const SCEV *AccessFn = SE->getSCEVAtScope(getPointerOperand(*Inst), L);
const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(AccessFn); const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(AccessFn);
// Do not try to delinearize memory accesses that are not AddRecs.
if (!AR) if (!AR)
break; break;

View File

@ -24,11 +24,11 @@
// Both of these are conservative weaknesses; // Both of these are conservative weaknesses;
// that is, not a source of correctness problems. // that is, not a source of correctness problems.
// //
// The implementation depends on the GEP instruction to // The implementation depends on the GEP instruction to differentiate
// differentiate subscripts. Since Clang linearizes subscripts // subscripts. Since Clang linearizes some array subscripts, the dependence
// for most arrays, we give up some precision (though the existing MIV tests // analysis is using SCEV->delinearize to recover the representation of multiple
// will help). We trust that the GEP instruction will eventually be extended. // subscripts, and thus avoid the more expensive and less precise MIV tests. The
// In the meantime, we should explore Maslov's ideas about delinearization. // delinearization is controlled by the flag -da-delinearize.
// //
// We should pay some careful attention to the possibility of integer overflow // We should pay some careful attention to the possibility of integer overflow
// in the implementation of the various tests. This could happen with Add, // in the implementation of the various tests. This could happen with Add,
@ -3206,10 +3206,21 @@ DependenceAnalysis::tryDelinearize(const SCEV *SrcSCEV, const SCEV *DstSCEV,
DEBUG(errs() << *DstSubscripts[i]); DEBUG(errs() << *DstSubscripts[i]);
#endif #endif
// The delinearization transforms a single-subscript MIV dependence test into
// a multi-subscript SIV dependence test that is easier to compute. So we
// resize Pair to contain as many pairs of subscripts as the delinearization
// has found, and then initialize the pairs following the delinearization.
Pair.resize(size); Pair.resize(size);
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
Pair[i].Src = SrcSubscripts[i]; Pair[i].Src = SrcSubscripts[i];
Pair[i].Dst = DstSubscripts[i]; Pair[i].Dst = DstSubscripts[i];
// FIXME: we should record the bounds SrcSizes[i] and DstSizes[i] that the
// delinearization has found, and add these constraints to the dependence
// check to avoid memory accesses overflow from one dimension into another.
// This is related to the problem of determining the existence of data
// dependences in array accesses using a different number of subscripts: in
// C one can access an array A[100][100]; as A[0][9999], *A[9999], etc.
} }
return true; return true;

View File

@ -7070,27 +7070,66 @@ private:
/// Splits the SCEV into two vectors of SCEVs representing the subscripts and /// Splits the SCEV into two vectors of SCEVs representing the subscripts and
/// sizes of an array access. Returns the remainder of the delinearization that /// sizes of an array access. Returns the remainder of the delinearization that
/// is the offset start of the array. For example /// is the offset start of the array. The SCEV->delinearize algorithm computes
/// delinearize ({(((-4 + (3 * %m)))),+,(%m)}<%for.i>) { /// the multiples of SCEV coefficients: that is a pattern matching of sub
/// IV: {0,+,1}<%for.i> /// expressions in the stride and base of a SCEV corresponding to the
/// Start: -4 + (3 * %m) /// computation of a GCD (greatest common divisor) of base and stride. When
/// Step: %m /// SCEV->delinearize fails, it returns the SCEV unchanged.
/// SCEVUDiv (Start, Step) = 3 remainder -4 ///
/// rem = delinearize (3) = 3 /// For example: when analyzing the memory access A[i][j][k] in this loop nest
/// Subscripts.push_back(IV + rem) ///
/// Sizes.push_back(Step) /// void foo(long n, long m, long o, double A[n][m][o]) {
/// return remainder -4 ///
/// 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;
/// } /// }
/// When delinearize fails, it returns the SCEV unchanged. ///
/// 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.
const SCEV * const SCEV *
SCEVAddRecExpr::delinearize(ScalarEvolution &SE, SCEVAddRecExpr::delinearize(ScalarEvolution &SE,
SmallVectorImpl<const SCEV *> &Subscripts, SmallVectorImpl<const SCEV *> &Subscripts,
SmallVectorImpl<const SCEV *> &Sizes) const { SmallVectorImpl<const SCEV *> &Sizes) const {
// Early exit in case this SCEV is not an affine multivariate function.
if (!this->isAffine()) if (!this->isAffine())
return this; return this;
const SCEV *Start = this->getStart(); const SCEV *Start = this->getStart();
const SCEV *Step = this->getStepRecurrence(SE); const SCEV *Step = this->getStepRecurrence(SE);
// Build the SCEV representation of the cannonical induction variable in the
// loop of this SCEV.
const SCEV *Zero = SE.getConstant(this->getType(), 0); const SCEV *Zero = SE.getConstant(this->getType(), 0);
const SCEV *One = SE.getConstant(this->getType(), 1); const SCEV *One = SE.getConstant(this->getType(), 1);
const SCEV *IV = const SCEV *IV =
@ -7098,38 +7137,55 @@ SCEVAddRecExpr::delinearize(ScalarEvolution &SE,
DEBUG(dbgs() << "(delinearize: " << *this << "\n"); DEBUG(dbgs() << "(delinearize: " << *this << "\n");
// Currently we fail to delinearize when the stride of this SCEV is 1. We
// could decide to not fail in this case: we could just return 1 for the size
// of the subscript, and this same SCEV for the access function.
if (Step == One) { if (Step == One) {
DEBUG(dbgs() << "failed to delinearize " << *this << "\n)\n"); DEBUG(dbgs() << "failed to delinearize " << *this << "\n)\n");
return this; return this;
} }
// Find the GCD and Remainder of the Start and Step coefficients of this SCEV.
const SCEV *Remainder = NULL; const SCEV *Remainder = NULL;
const SCEV *GCD = SCEVGCD::findGCD(SE, Start, Step, &Remainder); const SCEV *GCD = SCEVGCD::findGCD(SE, Start, Step, &Remainder);
DEBUG(dbgs() << "GCD: " << *GCD << "\n"); DEBUG(dbgs() << "GCD: " << *GCD << "\n");
DEBUG(dbgs() << "Remainder: " << *Remainder << "\n"); DEBUG(dbgs() << "Remainder: " << *Remainder << "\n");
// Same remark as above: we currently fail the delinearization, although we
// can very well handle this special case.
if (GCD == One) { if (GCD == One) {
DEBUG(dbgs() << "failed to delinearize " << *this << "\n)\n"); DEBUG(dbgs() << "failed to delinearize " << *this << "\n)\n");
return this; return this;
} }
// As findGCD computed Remainder, GCD divides "Start - Remainder." The
// Quotient is then this SCEV without Remainder, scaled down by the GCD. The
// Quotient is what will be used in the next subscript delinearization.
const SCEV *Quotient = const SCEV *Quotient =
SCEVDivision::divide(SE, SE.getMinusSCEV(Start, Remainder), GCD); SCEVDivision::divide(SE, SE.getMinusSCEV(Start, Remainder), GCD);
DEBUG(dbgs() << "Quotient: " << *Quotient << "\n"); DEBUG(dbgs() << "Quotient: " << *Quotient << "\n");
const SCEV *Rem; const SCEV *Rem;
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(Quotient)) if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(Quotient))
// Recursively call delinearize on the Quotient until there are no more
// multiples that can be recognized.
Rem = AR->delinearize(SE, Subscripts, Sizes); Rem = AR->delinearize(SE, Subscripts, Sizes);
else else
Rem = Quotient; Rem = Quotient;
// Scale up the cannonical induction variable IV by whatever remains from the
// Step after division by the GCD: the GCD is the size of all the sub-array.
if (Step != GCD) { if (Step != GCD) {
Step = SCEVDivision::divide(SE, Step, GCD); Step = SCEVDivision::divide(SE, Step, GCD);
IV = SE.getMulExpr(IV, Step); IV = SE.getMulExpr(IV, Step);
} }
// The access function in the current subscript is computed as the cannonical
// induction variable IV (potentially scaled up by the step) and offset by
// Rem, the offset of delinearization in the sub-array.
const SCEV *Index = SE.getAddExpr(IV, Rem); const SCEV *Index = SE.getAddExpr(IV, Rem);
// Record the access function and the size of the current subscript.
Subscripts.push_back(Index); Subscripts.push_back(Index);
Sizes.push_back(GCD); Sizes.push_back(GCD);