92
92
#include " llvm/Support/ErrorHandling.h"
93
93
#include < algorithm>
94
94
#include < cassert>
95
+ #include < llvm/Support/raw_ostream.h>
95
96
#include < memory>
96
97
#include < string>
97
98
#include < vector>
@@ -610,6 +611,222 @@ struct SchedRemainder {
610
611
void init (ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel);
611
612
};
612
613
614
+ // / ResourceSegments are a collection of intervals closed on the
615
+ // / left and opened on the right:
616
+ // /
617
+ // / list{ [a1, b1), [a2, b2), ..., [a_N, b_N) }
618
+ // /
619
+ // / The collection has the following properties:
620
+ // /
621
+ // / 1. The list is ordered: a_i < b_i and b_i < a_(i+1)
622
+ // /
623
+ // / 2. The intervals in the collection do not intersect each other.
624
+ // /
625
+ // / A \ref ResourceSegments instance represents the cycle
626
+ // / reservation history of the instance of and individual resource.
627
+ class ResourceSegments {
628
+ public:
629
+ // / Represents an interval of discrete integer values closed on
630
+ // / the left and open on the right: [a, b).
631
+ typedef std::pair<int64_t , int64_t > IntervalTy;
632
+
633
+ // / Adds an interval [a, b) to the collection of the instance.
634
+ // /
635
+ // / When adding [a, b[ to the collection, the operation merges the
636
+ // / adjacent intervals. For example
637
+ // /
638
+ // / 0 1 2 3 4 5 6 7 8 9 10
639
+ // / [-----) [--) [--)
640
+ // / + [--)
641
+ // / = [-----------) [--)
642
+ // /
643
+ // / To be able to debug duplicate resource usage, the function has
644
+ // / assertion that checks that no interval should be added if it
645
+ // / overlaps any of the intervals in the collection. We can
646
+ // / require this because by definition a \ref ResourceSegments is
647
+ // / attached only to an individual resource instance.
648
+ void add (IntervalTy A, const unsigned CutOff = 10 );
649
+
650
+ public:
651
+ // / Checks whether intervals intersect.
652
+ static bool intersects (IntervalTy A, IntervalTy B);
653
+
654
+ // / These function return the interval used by a resource in bottom and top
655
+ // / scheduling.
656
+ // /
657
+ // / Consider an instruction that uses resources X0, X1 and X2 as follows:
658
+ // /
659
+ // / X0 X1 X1 X2 +--------+------------+------+
660
+ // / |Resource|StartAtCycle|Cycles|
661
+ // / +--------+------------+------+
662
+ // / | X0 | 0 | 1 |
663
+ // / +--------+------------+------+
664
+ // / | X1 | 1 | 3 |
665
+ // / +--------+------------+------+
666
+ // / | X2 | 3 | 4 |
667
+ // / +--------+------------+------+
668
+ // /
669
+ // / If we can schedule the instruction at cycle C, we need to
670
+ // / compute the interval of the resource as follows:
671
+ // /
672
+ // / # TOP DOWN SCHEDULING
673
+ // /
674
+ // / Cycles scheduling flows to the _right_, in the same direction
675
+ // / of time.
676
+ // /
677
+ // / C 1 2 3 4 5 ...
678
+ // / ------|------|------|------|------|------|----->
679
+ // / X0 X1 X1 X2 ---> direction of time
680
+ // / X0 [C, C+1)
681
+ // / X1 [C+1, C+3)
682
+ // / X2 [C+3, C+4)
683
+ // /
684
+ // / Therefore, the formula to compute the interval for a resource
685
+ // / of an instruction that can be scheduled at cycle C in top-down
686
+ // / scheduling is:
687
+ // /
688
+ // / [C+StartAtCycle, C+Cycles)
689
+ // /
690
+ // /
691
+ // / # BOTTOM UP SCHEDULING
692
+ // /
693
+ // / Cycles scheduling flows to the _left_, in opposite direction
694
+ // / of time.
695
+ // /
696
+ // / In bottom up scheduling, the scheduling happens in opposite
697
+ // / direction to the execution of the cycles of the
698
+ // / instruction. When the instruction is scheduled at cycle `C`,
699
+ // / the resources are allocated in the past relative to `C`:
700
+ // /
701
+ // / 2 1 C -1 -2 -3 -4 -5 ...
702
+ // / <-----|------|------|------|------|------|------|------|---
703
+ // / X0 X1 X1 X2 ---> direction of time
704
+ // / X0 (C+1, C]
705
+ // / X1 (C, C-2]
706
+ // / X2 (C-2, C-3]
707
+ // /
708
+ // / Therefore, the formula to compute the interval for a resource
709
+ // / of an instruction that can be scheduled at cycle C in bottom-up
710
+ // / scheduling is:
711
+ // /
712
+ // / [C-Cycle+1, C-StartAtCycle+1)
713
+ // /
714
+ // /
715
+ // / NOTE: In both cases, the number of cycles booked by a
716
+ // / resources is the value (Cycle - StartAtCycles).
717
+ static IntervalTy getResourceIntervalBottom (unsigned C, unsigned StartAtCycle,
718
+ unsigned Cycle) {
719
+ return std::make_pair<long , long >((long )C - (long )Cycle + 1L ,
720
+ (long )C - (long )StartAtCycle + 1L );
721
+ }
722
+ static IntervalTy getResourceIntervalTop (unsigned C, unsigned StartAtCycle,
723
+ unsigned Cycle) {
724
+ return std::make_pair<long , long >((long )C + (long )StartAtCycle,
725
+ (long )C + (long )Cycle);
726
+ }
727
+
728
+ private:
729
+ // / Finds the first cycle in which a resource can be allocated.
730
+ // /
731
+ // / The function uses the \param IntervalBuider [*] to build a
732
+ // / resource interval [a, b[ out of the input parameters \param
733
+ // / CurrCycle, \param StartAtCycle and \param Cycle.
734
+ // /
735
+ // / The function then loops through the intervals in the ResourceSegments
736
+ // / and shifts the interval [a, b[ and the ReturnCycle to the
737
+ // / right until there is no intersection between the intervals of
738
+ // / the \ref ResourceSegments instance and the new shifted [a, b[. When
739
+ // / this condition is met, the ReturnCycle (which
740
+ // / correspond to the cycle in which the resource can be
741
+ // / allocated) is returned.
742
+ // /
743
+ // / c = CurrCycle in input
744
+ // / c 1 2 3 4 5 6 7 8 9 10 ... ---> (time
745
+ // / flow)
746
+ // / ResourceSegments... [---) [-------) [-----------)
747
+ // / c [1 3[ -> StartAtCycle=1, Cycles=3
748
+ // / ++c [1 3)
749
+ // / ++c [1 3)
750
+ // / ++c [1 3)
751
+ // / ++c [1 3)
752
+ // / ++c [1 3) ---> returns c
753
+ // / incremented by 5 (c+5)
754
+ // /
755
+ // /
756
+ // / Notice that for bottom-up scheduling the diagram is slightly
757
+ // / different because the current cycle c is always on the right
758
+ // / of the interval [a, b) (see \ref
759
+ // / `getResourceIntervalBottom`). This is because the cycle
760
+ // / increments for bottom-up scheduling moved in the direction
761
+ // / opposite to the direction of time:
762
+ // /
763
+ // / --------> direction of time.
764
+ // / XXYZZZ (resource usage)
765
+ // / --------> direction of top-down execution cycles.
766
+ // / <-------- direction of bottom-up execution cycles.
767
+ // /
768
+ // / Even though bottom-up scheduling moves against the flow of
769
+ // / time, the algorithm used to find the first free slot in between
770
+ // / intervals is the same as for top-down scheduling.
771
+ // /
772
+ // / [*] See \ref `getResourceIntervalTop` and
773
+ // / \ref `getResourceIntervalBottom` to see how such resource intervals
774
+ // / are built.
775
+ unsigned
776
+ getFirstAvailableAt (unsigned CurrCycle, unsigned StartAtCycle, unsigned Cycle,
777
+ std::function<IntervalTy(unsigned , unsigned , unsigned )>
778
+ IntervalBuilder) const ;
779
+
780
+ public:
781
+ // / getFirstAvailableAtFromBottom and getFirstAvailableAtFromTop
782
+ // / should be merged in a single function in which a function that
783
+ // / creates the `NewInterval` is passed as a parameter.
784
+ unsigned getFirstAvailableAtFromBottom (unsigned CurrCycle,
785
+ unsigned StartAtCycle,
786
+ unsigned Cycle) const {
787
+ return getFirstAvailableAt (CurrCycle, StartAtCycle, Cycle,
788
+ getResourceIntervalBottom);
789
+ }
790
+ unsigned getFirstAvailableAtFromTop (unsigned CurrCycle, unsigned StartAtCycle,
791
+ unsigned Cycle) const {
792
+ return getFirstAvailableAt (CurrCycle, StartAtCycle, Cycle,
793
+ getResourceIntervalTop);
794
+ }
795
+
796
+ private:
797
+ std::list<IntervalTy> _Intervals;
798
+ // / Merge all adjacent intervals in the collection. For all pairs
799
+ // / of adjacient intervals, it performs [a, b) + [b, c) -> [a, c).
800
+ // /
801
+ // / Before performing the merge operation, the intervals are
802
+ // / sorted with \ref sort_predicate.
803
+ void sortAndMerge ();
804
+
805
+ public:
806
+ // constructor for empty set
807
+ explicit ResourceSegments (){};
808
+ bool empty () const { return _Intervals.empty (); }
809
+ explicit ResourceSegments (std::list<IntervalTy> Intervals)
810
+ : _Intervals(Intervals) {
811
+ sortAndMerge ();
812
+ }
813
+
814
+ friend bool operator ==(const ResourceSegments &c1,
815
+ const ResourceSegments &c2) {
816
+ return c1._Intervals == c2._Intervals ;
817
+ }
818
+ #ifndef NDEBUG
819
+ friend llvm::raw_ostream &operator <<(llvm::raw_ostream &os,
820
+ const ResourceSegments &Segments) {
821
+ os << " { " ;
822
+ for (auto p : Segments._Intervals )
823
+ os << " [" << p.first << " , " << p.second << " ), " ;
824
+ os << " }\n " ;
825
+ return os;
826
+ }
827
+ #endif
828
+ };
829
+
613
830
// / Each Scheduling boundary is associated with ready queues. It tracks the
614
831
// / current cycle in the direction of movement, and maintains the state
615
832
// / of "hazards" and other interlocks at the current cycle.
@@ -675,12 +892,14 @@ class SchedBoundary {
675
892
// Is the scheduled region resource limited vs. latency limited.
676
893
bool IsResourceLimited;
677
894
678
- // Record the highest cycle at which each resource has been reserved by a
679
- // scheduled instruction.
680
- SmallVector<unsigned , 16 > ReservedCycles;
681
-
682
- // / For each PIdx, stores first index into ReservedCycles that corresponds to
683
- // / it.
895
+ public:
896
+ private:
897
+ // / Record how resources have been allocated across the cycles of
898
+ // / the execution.
899
+ std::map<unsigned , ResourceSegments> ReservedResourceSegments;
900
+ std::vector<unsigned > ReservedCycles;
901
+ // / For each PIdx, stores first index into ReservedResourceSegments that
902
+ // / corresponds to it.
684
903
// /
685
904
// / For example, consider the following 3 resources (ResourceCount =
686
905
// / 3):
@@ -696,12 +915,14 @@ class SchedBoundary {
696
915
// / +------------+--------+
697
916
// /
698
917
// / In this case, the total number of resource instances is 6. The
699
- // / vector \ref ReservedCycles will have a slot for each instance. The
700
- // / vector \ref ReservedCyclesIndex will track at what index the first
918
+ // / vector \ref ReservedResourceSegments will have a slot for each instance.
919
+ // / The vector \ref ReservedCyclesIndex will track at what index the first
701
920
// / instance of the resource is found in the vector of \ref
702
- // / ReservedCycles:
921
+ // / ReservedResourceSegments:
922
+ // /
923
+ // / Indexes of instances in
924
+ // / ReservedResourceSegments
703
925
// /
704
- // / Indexes of instances in ReservedCycles
705
926
// / 0 1 2 3 4 5
706
927
// / ReservedCyclesIndex[0] = 0; [X0, X1,
707
928
// / ReservedCyclesIndex[1] = 2; Y0, Y1, Y2
@@ -787,11 +1008,13 @@ class SchedBoundary {
787
1008
unsigned getLatencyStallCycles (SUnit *SU);
788
1009
789
1010
unsigned getNextResourceCycleByInstance (unsigned InstanceIndex,
790
- unsigned Cycles);
1011
+ unsigned Cycles,
1012
+ unsigned StartAtCycle);
791
1013
792
1014
std::pair<unsigned , unsigned > getNextResourceCycle (const MCSchedClassDesc *SC,
793
1015
unsigned PIdx,
794
- unsigned Cycles);
1016
+ unsigned Cycles,
1017
+ unsigned StartAtCycle);
795
1018
796
1019
bool isUnbufferedGroup (unsigned PIdx) const {
797
1020
return SchedModel->getProcResource (PIdx)->SubUnitsIdxBegin &&
@@ -820,7 +1043,8 @@ class SchedBoundary {
820
1043
void incExecutedResources (unsigned PIdx, unsigned Count);
821
1044
822
1045
unsigned countResource (const MCSchedClassDesc *SC, unsigned PIdx,
823
- unsigned Cycles, unsigned ReadyCycle);
1046
+ unsigned Cycles, unsigned ReadyCycle,
1047
+ unsigned StartAtCycle);
824
1048
825
1049
void bumpNode (SUnit *SU);
826
1050
0 commit comments