2424#include " llvm/IR/InstIterator.h"
2525#include " llvm/IR/Instructions.h"
2626#include " llvm/IR/PassManager.h"
27+ #include " llvm/Support/CommandLine.h"
2728#include " llvm/Support/Debug.h"
2829#include " llvm/Support/raw_ostream.h"
2930
@@ -32,6 +33,11 @@ using namespace llvm;
3233#define DL_NAME " delinearize"
3334#define DEBUG_TYPE DL_NAME
3435
36+ static cl::opt<bool > UseFixedSizeArrayHeuristic (
37+ " delinearize-use-fixed-size-array-heuristic" , cl::init(false ), cl::Hidden,
38+ cl::desc(" When printing analysis, use the heuristic for fixed-size arrays "
39+ " if the default delinearizetion fails." ));
40+
3541// Return true when S contains at least an undef value.
3642static inline bool containsUndefs (const SCEV *S) {
3743 return SCEVExprContains (S, [](const SCEV *S) {
@@ -480,6 +486,178 @@ void llvm::delinearize(ScalarEvolution &SE, const SCEV *Expr,
480486 });
481487}
482488
489+ static std::optional<APInt> tryIntoAPInt (const SCEV *S) {
490+ if (const auto *Const = dyn_cast<SCEVConstant>(S))
491+ return Const->getAPInt ();
492+ return std::nullopt ;
493+ }
494+
495+ // / Collects the absolute values of constant steps for all induction variables.
496+ // / Returns true if we can prove that all step recurrences are constants and \p
497+ // / Expr is dividable by \p ElementSize. Each step recurrence is stored in \p
498+ // / Steps after divided by \p ElementSize.
499+ static bool collectConstantAbsSteps (ScalarEvolution &SE, const SCEV *Expr,
500+ SmallVectorImpl<unsigned > &Steps,
501+ unsigned ElementSize) {
502+ // End of recursion. The constant value also must be a multiple of
503+ // ElementSize.
504+ if (const auto *Const = dyn_cast<SCEVConstant>(Expr)) {
505+ const unsigned Mod = Const->getAPInt ().urem (ElementSize);
506+ return Mod == 0 ;
507+ }
508+
509+ const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(Expr);
510+ if (!AR || !AR->isAffine ())
511+ return false ;
512+
513+ const SCEV *Step = AR->getStepRecurrence (SE);
514+ std::optional<APInt> StepAPInt = tryIntoAPInt (Step);
515+ if (!StepAPInt)
516+ return false ;
517+
518+ APInt Q;
519+ uint64_t R;
520+ APInt::udivrem (StepAPInt->abs (), ElementSize, Q, R);
521+ if (R != 0 )
522+ return false ;
523+
524+ // Bail out when the step is too large.
525+ std::optional<unsigned > StepVal = Q.tryZExtValue ();
526+ if (!StepVal)
527+ return false ;
528+
529+ Steps.push_back (*StepVal);
530+ return collectConstantAbsSteps (SE, AR->getStart (), Steps, ElementSize);
531+ }
532+
533+ static bool findFixedSizeArrayDimensions (ScalarEvolution &SE, const SCEV *Expr,
534+ SmallVectorImpl<unsigned > &Sizes,
535+ const SCEV *ElementSize) {
536+ if (!ElementSize)
537+ return false ;
538+
539+ std::optional<APInt> ElementSizeAPInt = tryIntoAPInt (ElementSize);
540+ if (!ElementSizeAPInt || *ElementSizeAPInt == 0 )
541+ return false ;
542+
543+ std::optional<unsigned > ElementSizeConst = ElementSizeAPInt->tryZExtValue ();
544+
545+ // Early exit when ElementSize is not a positive constant.
546+ if (!ElementSizeConst)
547+ return false ;
548+
549+ if (!collectConstantAbsSteps (SE, Expr, Sizes, *ElementSizeConst) ||
550+ Sizes.empty ()) {
551+ Sizes.clear ();
552+ return false ;
553+ }
554+
555+ // At this point, Sizes contains the absolute step recurrences for all
556+ // induction variables. Each step recurrence must be a multiple of the size of
557+ // the array element. Assuming that the each value represents the size of an
558+ // array for each dimension, attempts to restore the length of each dimension
559+ // by dividing the step recurrence by the next smaller value. For example, if
560+ // we have the following AddRec SCEV:
561+ //
562+ // AddRec: {{{0,+,2048}<%for.i>,+,256}<%for.j>,+,8}<%for.k> (ElementSize=8)
563+ //
564+ // Then Sizes will become [256, 32, 1] after sorted. We don't know the size of
565+ // the outermost dimension, the next dimension will be computed as 256 / 32 =
566+ // 8, and the last dimension will be computed as 32 / 1 = 32. Thus it results
567+ // in like Arr[UnknownSize][8][32] with elements of size 8 bytes, where Arr is
568+ // a base pointer.
569+ //
570+ // TODO: Catch more cases, e.g., when a step recurrence is not dividable by
571+ // the next smaller one, like A[i][3*j].
572+ llvm::sort (Sizes.rbegin (), Sizes.rend ());
573+ Sizes.erase (llvm::unique (Sizes), Sizes.end ());
574+ for (unsigned I = 0 ; I + 1 < Sizes.size (); I++) {
575+ unsigned PrevSize = Sizes[I + 1 ];
576+ if (Sizes[I] % PrevSize) {
577+ Sizes.clear ();
578+ return false ;
579+ }
580+ Sizes[I] /= PrevSize;
581+ }
582+
583+ // The last element should be ElementSize.
584+ Sizes.back () = *ElementSizeConst;
585+ return true ;
586+ }
587+
588+ // / Splits the SCEV into two vectors of SCEVs representing the subscripts and
589+ // / sizes of an array access, assuming that the array is a fixed size array.
590+ // /
591+ // / E.g., if we have the code like as follows:
592+ // /
593+ // / double A[42][8][32];
594+ // / for i
595+ // / for j
596+ // / for k
597+ // / use A[i][j][k]
598+ // /
599+ // / The access function will be represented as an AddRec SCEV like:
600+ // /
601+ // / AddRec: {{{0,+,2048}<%for.i>,+,256}<%for.j>,+,8}<%for.k> (ElementSize=8)
602+ // /
603+ // / Then findFixedSizeArrayDimensions infers the size of each dimension of the
604+ // / array based on the fact that the value of the step recurrence is a multiple
605+ // / of the size of the corresponding array element. In the above example, it
606+ // / results in the following:
607+ // /
608+ // / CHECK: ArrayDecl[UnknownSize][8][32] with elements of 8 bytes.
609+ // /
610+ // / Finally each subscript will be computed as follows:
611+ // /
612+ // / CHECK: ArrayRef[{0,+,1}<%for.i>][{0,+,1}<%for.j>][{0,+,1}<%for.k>]
613+ // /
614+ // / Note that this function doesn't check the range of possible values for each
615+ // / subscript, so the caller should perform additional boundary checks if
616+ // / necessary.
617+ // /
618+ // / TODO: At the moment, this function can handle only simple cases. For
619+ // / example, we cannot handle a case where a step recurrence is not dividable by
620+ // / the next smaller step recurrence, e.g., A[i][3*j]. Furthermore, this
621+ // / function doesn't guarantee that the original array size is restored
622+ // / "correctly". For example, in the following case:
623+ // /
624+ // / double A[42][4][32];
625+ // / double B[42][8][64];
626+ // / for i
627+ // / for j
628+ // / for k
629+ // / use A[i][j][k]
630+ // / use B[i][2*j][k]
631+ // /
632+ // / The access function for both accesses will be the same:
633+ // /
634+ // / AddRec: {{{0,+,2048}<%for.i>,+,512}<%for.j>,+,8}<%for.k> (ElementSize=8)
635+ // /
636+ // / The array sizes for both A and B will be computed as
637+ // / ArrayDecl[UnknownSize][4][64], which matches for A, but not for B.
638+ void llvm::delinearizeFixedSizeArray (ScalarEvolution &SE, const SCEV *Expr,
639+ SmallVectorImpl<const SCEV *> &Subscripts,
640+ SmallVectorImpl<const SCEV *> &Sizes,
641+ const SCEV *ElementSize) {
642+
643+ // First step: find the fixed array size.
644+ SmallVector<unsigned , 4 > ConstSizes;
645+ if (!findFixedSizeArrayDimensions (SE, Expr, ConstSizes, ElementSize)) {
646+ Sizes.clear ();
647+ return ;
648+ }
649+
650+ // Convert the constant size to SCEV.
651+ for (unsigned Size : ConstSizes)
652+ Sizes.push_back (SE.getConstant (Expr->getType (), Size));
653+
654+ // Second step: compute the access functions for each subscript.
655+ computeAccessFunctions (SE, Expr, Subscripts, Sizes);
656+
657+ if (Subscripts.empty ())
658+ return ;
659+ }
660+
483661bool llvm::getIndexExpressionsFromGEP (ScalarEvolution &SE,
484662 const GetElementPtrInst *GEP,
485663 SmallVectorImpl<const SCEV *> &Subscripts,
@@ -586,9 +764,21 @@ void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,
586764 O << " AccessFunction: " << *AccessFn << " \n " ;
587765
588766 SmallVector<const SCEV *, 3 > Subscripts, Sizes;
767+
768+ auto IsDelinearizationFailed = [&]() {
769+ return Subscripts.size () == 0 || Sizes.size () == 0 ||
770+ Subscripts.size () != Sizes.size ();
771+ };
772+
589773 delinearize (*SE, AccessFn, Subscripts, Sizes, SE->getElementSize (&Inst));
590- if (Subscripts.size () == 0 || Sizes.size () == 0 ||
591- Subscripts.size () != Sizes.size ()) {
774+ if (UseFixedSizeArrayHeuristic && IsDelinearizationFailed ()) {
775+ Subscripts.clear ();
776+ Sizes.clear ();
777+ delinearizeFixedSizeArray (*SE, AccessFn, Subscripts, Sizes,
778+ SE->getElementSize (&Inst));
779+ }
780+
781+ if (IsDelinearizationFailed ()) {
592782 O << " failed to delinearize\n " ;
593783 continue ;
594784 }
0 commit comments