24
24
#include " llvm/IR/InstIterator.h"
25
25
#include " llvm/IR/Instructions.h"
26
26
#include " llvm/IR/PassManager.h"
27
+ #include " llvm/Support/CommandLine.h"
27
28
#include " llvm/Support/Debug.h"
28
29
#include " llvm/Support/raw_ostream.h"
29
30
@@ -32,6 +33,11 @@ using namespace llvm;
32
33
#define DL_NAME " delinearize"
33
34
#define DEBUG_TYPE DL_NAME
34
35
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
+
35
41
// Return true when S contains at least an undef value.
36
42
static inline bool containsUndefs (const SCEV *S) {
37
43
return SCEVExprContains (S, [](const SCEV *S) {
@@ -480,6 +486,178 @@ void llvm::delinearize(ScalarEvolution &SE, const SCEV *Expr,
480
486
});
481
487
}
482
488
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
+
483
661
bool llvm::getIndexExpressionsFromGEP (ScalarEvolution &SE,
484
662
const GetElementPtrInst *GEP,
485
663
SmallVectorImpl<const SCEV *> &Subscripts,
@@ -586,9 +764,21 @@ void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,
586
764
O << " AccessFunction: " << *AccessFn << " \n " ;
587
765
588
766
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
+
589
773
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 ()) {
592
782
O << " failed to delinearize\n " ;
593
783
continue ;
594
784
}
0 commit comments