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,184 @@ 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 divisible 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<uint64_t > &Steps,
501+ uint64_t 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 uint64_t 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<uint64_t > 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+ bool llvm::findFixedSizeArrayDimensions (ScalarEvolution &SE, const SCEV *Expr,
534+ SmallVectorImpl<uint64_t > &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<uint64_t > 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 divisible 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+
575+ // The last element in Sizes should be ElementSize. At this point, all values
576+ // in Sizes are assumed to be divided by ElementSize, so replace it with 1.
577+ assert (Sizes.back () != 0 && " Unexpected zero size in Sizes." );
578+ Sizes.back () = 1 ;
579+
580+ for (unsigned I = 0 ; I + 1 < Sizes.size (); I++) {
581+ uint64_t PrevSize = Sizes[I + 1 ];
582+ if (Sizes[I] % PrevSize) {
583+ Sizes.clear ();
584+ return false ;
585+ }
586+ Sizes[I] /= PrevSize;
587+ }
588+
589+ // Finally, the last element in Sizes should be ElementSize.
590+ Sizes.back () = *ElementSizeConst;
591+ return true ;
592+ }
593+
594+ // / Splits the SCEV into two vectors of SCEVs representing the subscripts and
595+ // / sizes of an array access, assuming that the array is a fixed size array.
596+ // /
597+ // / E.g., if we have the code like as follows:
598+ // /
599+ // / double A[42][8][32];
600+ // / for i
601+ // / for j
602+ // / for k
603+ // / use A[i][j][k]
604+ // /
605+ // / The access function will be represented as an AddRec SCEV like:
606+ // /
607+ // / AddRec: {{{0,+,2048}<%for.i>,+,256}<%for.j>,+,8}<%for.k> (ElementSize=8)
608+ // /
609+ // / Then findFixedSizeArrayDimensions infers the size of each dimension of the
610+ // / array based on the fact that the value of the step recurrence is a multiple
611+ // / of the size of the corresponding array element. In the above example, it
612+ // / results in the following:
613+ // /
614+ // / CHECK: ArrayDecl[UnknownSize][8][32] with elements of 8 bytes.
615+ // /
616+ // / Finally each subscript will be computed as follows:
617+ // /
618+ // / CHECK: ArrayRef[{0,+,1}<%for.i>][{0,+,1}<%for.j>][{0,+,1}<%for.k>]
619+ // /
620+ // / Note that this function doesn't check the range of possible values for each
621+ // / subscript, so the caller should perform additional boundary checks if
622+ // / necessary.
623+ // /
624+ // / Also note that this function doesn't guarantee that the original array size
625+ // / is restored "correctly". For example, in the following case:
626+ // /
627+ // / double A[42][4][64];
628+ // / double B[42][8][32];
629+ // / for i
630+ // / for j
631+ // / for k
632+ // / use A[i][j][k]
633+ // / use B[i][2*j][k]
634+ // /
635+ // / The access function for both accesses will be the same:
636+ // /
637+ // / AddRec: {{{0,+,2048}<%for.i>,+,512}<%for.j>,+,8}<%for.k> (ElementSize=8)
638+ // /
639+ // / The array sizes for both A and B will be computed as
640+ // / ArrayDecl[UnknownSize][4][64], which matches for A, but not for B.
641+ // /
642+ // / TODO: At the moment, this function can handle only simple cases. For
643+ // / example, we cannot handle a case where a step recurrence is not divisible
644+ // / by the next smaller step recurrence, e.g., A[i][3*j].
645+ bool llvm::delinearizeFixedSizeArray (ScalarEvolution &SE, const SCEV *Expr,
646+ SmallVectorImpl<const SCEV *> &Subscripts,
647+ SmallVectorImpl<const SCEV *> &Sizes,
648+ const SCEV *ElementSize) {
649+
650+ // First step: find the fixed array size.
651+ SmallVector<uint64_t , 4 > ConstSizes;
652+ if (!findFixedSizeArrayDimensions (SE, Expr, ConstSizes, ElementSize)) {
653+ Sizes.clear ();
654+ return false ;
655+ }
656+
657+ // Convert the constant size to SCEV.
658+ for (uint64_t Size : ConstSizes)
659+ Sizes.push_back (SE.getConstant (Expr->getType (), Size));
660+
661+ // Second step: compute the access functions for each subscript.
662+ computeAccessFunctions (SE, Expr, Subscripts, Sizes);
663+
664+ return !Subscripts.empty ();
665+ }
666+
483667bool llvm::getIndexExpressionsFromGEP (ScalarEvolution &SE,
484668 const GetElementPtrInst *GEP,
485669 SmallVectorImpl<const SCEV *> &Subscripts,
@@ -586,9 +770,21 @@ void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,
586770 O << " AccessFunction: " << *AccessFn << " \n " ;
587771
588772 SmallVector<const SCEV *, 3 > Subscripts, Sizes;
773+
774+ auto IsDelinearizationFailed = [&]() {
775+ return Subscripts.size () == 0 || Sizes.size () == 0 ||
776+ Subscripts.size () != Sizes.size ();
777+ };
778+
589779 delinearize (*SE, AccessFn, Subscripts, Sizes, SE->getElementSize (&Inst));
590- if (Subscripts.size () == 0 || Sizes.size () == 0 ||
591- Subscripts.size () != Sizes.size ()) {
780+ if (UseFixedSizeArrayHeuristic && IsDelinearizationFailed ()) {
781+ Subscripts.clear ();
782+ Sizes.clear ();
783+ delinearizeFixedSizeArray (*SE, AccessFn, Subscripts, Sizes,
784+ SE->getElementSize (&Inst));
785+ }
786+
787+ if (IsDelinearizationFailed ()) {
592788 O << " failed to delinearize\n " ;
593789 continue ;
594790 }
0 commit comments