Revert "[MISched] Introduce and use ResourceSegments."

Reverted because it produces the following builbot failure at https://lab.llvm.org/buildbot#builders/9/builds/27319:

/b/ml-opt-rel-x86-64-b1/llvm-project/llvm/unittests/CodeGen/SchedBoundary.cpp: In member function ‘virtual void ResourceSegments_getFirstAvailableAtFromBottom_empty_Test::TestBody()’:
/b/ml-opt-rel-x86-64-b1/llvm-project/llvm/unittests/CodeGen/SchedBoundary.cpp:395:31: error: call of overloaded ‘ResourceSegments(<brace-enclosed initializer list>)’ is ambiguous
 395 |   auto X = ResourceSegments({});
     |                               ^

This reverts commit dc312f0331309692e8d6e06e93b3492b6a40989f.
This commit is contained in:
Francesco Petrogalli 2023-06-09 13:21:52 +02:00
parent 6680d60dd6
commit f1d1ca3d74
9 changed files with 37 additions and 829 deletions

View File

@ -92,7 +92,6 @@
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
#include <cassert>
#include <llvm/Support/raw_ostream.h>
#include <memory>
#include <string>
#include <vector>
@ -611,222 +610,6 @@ struct SchedRemainder {
void init(ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel);
};
/// ResourceSegments are a collection of intervals closed on the
/// left and opened on the right:
///
/// list{ [a1, b1), [a2, b2), ..., [a_N, b_N) }
///
/// The collection has the following properties:
///
/// 1. The list is ordered: a_i < b_i and b_i < a_(i+1)
///
/// 2. The intervals in the collection do not intersect each other.
///
/// A \ref ResourceSegments instance represents the cycle
/// reservation history of the instance of and individual resource.
class ResourceSegments {
public:
/// Represents an interval of discrete integer values closed on
/// the left and open on the right: [a, b).
typedef std::pair<int64_t, int64_t> IntervalTy;
/// Adds an interval [a, b) to the collection of the instance.
///
/// When adding [a, b[ to the collection, the operation merges the
/// adjacent intervals. For example
///
/// 0 1 2 3 4 5 6 7 8 9 10
/// [-----) [--) [--)
/// + [--)
/// = [-----------) [--)
///
/// To be able to debug duplicate resource usage, the function has
/// assertion that checks that no interval should be added if it
/// overlaps any of the intervals in the collection. We can
/// require this because by definition a \ref ResourceSegments is
/// attached only to an individual resource instance.
void add(IntervalTy A, const unsigned CutOff = 10);
public:
/// Checks whether intervals intersect.
static bool intersects(IntervalTy A, IntervalTy B);
/// These function return the interval used by a resource in bottom and top
/// scheduling.
///
/// Consider an instruction that uses resources X0, X1 and X2 as follows:
///
/// X0 X1 X1 X2 +--------+------------+------+
/// |Resource|StartAtCycle|Cycles|
/// +--------+------------+------+
/// | X0 | 0 | 1 |
/// +--------+------------+------+
/// | X1 | 1 | 3 |
/// +--------+------------+------+
/// | X2 | 3 | 4 |
/// +--------+------------+------+
///
/// If we can schedule the instruction at cycle C, we need to
/// compute the interval of the resource as follows:
///
/// # TOP DOWN SCHEDULING
///
/// Cycles scheduling flows to the _right_, in the same direction
/// of time.
///
/// C 1 2 3 4 5 ...
/// ------|------|------|------|------|------|----->
/// X0 X1 X1 X2 ---> direction of time
/// X0 [C, C+1)
/// X1 [C+1, C+3)
/// X2 [C+3, C+4)
///
/// Therefore, the formula to compute the interval for a resource
/// of an instruction that can be scheduled at cycle C in top-down
/// scheduling is:
///
/// [C+StartAtCycle, C+Cycles)
///
///
/// # BOTTOM UP SCHEDULING
///
/// Cycles scheduling flows to the _left_, in opposite direction
/// of time.
///
/// In bottom up scheduling, the scheduling happens in opposite
/// direction to the execution of the cycles of the
/// instruction. When the instruction is scheduled at cycle `C`,
/// the resources are allocated in the past relative to `C`:
///
/// 2 1 C -1 -2 -3 -4 -5 ...
/// <-----|------|------|------|------|------|------|------|---
/// X0 X1 X1 X2 ---> direction of time
/// X0 (C+1, C]
/// X1 (C, C-2]
/// X2 (C-2, C-3]
///
/// Therefore, the formula to compute the interval for a resource
/// of an instruction that can be scheduled at cycle C in bottom-up
/// scheduling is:
///
/// [C-Cycle+1, C-StartAtCycle+1)
///
///
/// NOTE: In both cases, the number of cycles booked by a
/// resources is the value (Cycle - StartAtCycles).
static IntervalTy getResourceIntervalBottom(unsigned C, unsigned StartAtCycle,
unsigned Cycle) {
return std::make_pair<long, long>((long)C - (long)Cycle + 1L,
(long)C - (long)StartAtCycle + 1L);
}
static IntervalTy getResourceIntervalTop(unsigned C, unsigned StartAtCycle,
unsigned Cycle) {
return std::make_pair<long, long>((long)C + (long)StartAtCycle,
(long)C + (long)Cycle);
}
private:
/// Finds the first cycle in which a resource can be allocated.
///
/// The function uses the \param IntervalBuider [*] to build a
/// resource interval [a, b[ out of the input parameters \param
/// CurrCycle, \param StartAtCycle and \param Cycle.
///
/// The function then loops through the intervals in the ResourceSegments
/// and shifts the interval [a, b[ and the ReturnCycle to the
/// right until there is no intersection between the intervals of
/// the \ref ResourceSegments instance and the new shifted [a, b[. When
/// this condition is met, the ReturnCycle (which
/// correspond to the cycle in which the resource can be
/// allocated) is returned.
///
/// c = CurrCycle in input
/// c 1 2 3 4 5 6 7 8 9 10 ... ---> (time
/// flow)
/// ResourceSegments... [---) [-------) [-----------)
/// c [1 3[ -> StartAtCycle=1, Cycles=3
/// ++c [1 3)
/// ++c [1 3)
/// ++c [1 3)
/// ++c [1 3)
/// ++c [1 3) ---> returns c
/// incremented by 5 (c+5)
///
///
/// Notice that for bottom-up scheduling the diagram is slightly
/// different because the current cycle c is always on the right
/// of the interval [a, b) (see \ref
/// `getResourceIntervalBottom`). This is because the cycle
/// increments for bottom-up scheduling moved in the direction
/// opposite to the direction of time:
///
/// --------> direction of time.
/// XXYZZZ (resource usage)
/// --------> direction of top-down execution cycles.
/// <-------- direction of bottom-up execution cycles.
///
/// Even though bottom-up scheduling moves against the flow of
/// time, the algorithm used to find the first free slot in between
/// intervals is the same as for top-down scheduling.
///
/// [*] See \ref `getResourceIntervalTop` and
/// \ref `getResourceIntervalBottom` to see how such resource intervals
/// are built.
unsigned
getFirstAvailableAt(unsigned CurrCycle, unsigned StartAtCycle, unsigned Cycle,
std::function<IntervalTy(unsigned, unsigned, unsigned)>
IntervalBuilder) const;
public:
/// getFirstAvailableAtFromBottom and getFirstAvailableAtFromTop
/// should be merged in a single function in which a function that
/// creates the `NewInterval` is passed as a parameter.
unsigned getFirstAvailableAtFromBottom(unsigned CurrCycle,
unsigned StartAtCycle,
unsigned Cycle) const {
return getFirstAvailableAt(CurrCycle, StartAtCycle, Cycle,
getResourceIntervalBottom);
}
unsigned getFirstAvailableAtFromTop(unsigned CurrCycle, unsigned StartAtCycle,
unsigned Cycle) const {
return getFirstAvailableAt(CurrCycle, StartAtCycle, Cycle,
getResourceIntervalTop);
}
private:
std::list<IntervalTy> _Intervals;
/// Merge all adjacent intervals in the collection. For all pairs
/// of adjacient intervals, it performs [a, b) + [b, c) -> [a, c).
///
/// Before performing the merge operation, the intervals are
/// sorted with \ref sort_predicate.
void sortAndMerge();
public:
// constructor for empty set
explicit ResourceSegments(){};
bool empty() const { return _Intervals.empty(); }
explicit ResourceSegments(std::list<IntervalTy> Intervals)
: _Intervals(Intervals) {
sortAndMerge();
}
friend bool operator==(const ResourceSegments &c1,
const ResourceSegments &c2) {
return c1._Intervals == c2._Intervals;
}
#ifndef NDEBUG
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
const ResourceSegments &Segments) {
os << "{ ";
for (auto p : Segments._Intervals)
os << "[" << p.first << ", " << p.second << "), ";
os << "}\n";
return os;
}
#endif
};
/// Each Scheduling boundary is associated with ready queues. It tracks the
/// current cycle in the direction of movement, and maintains the state
/// of "hazards" and other interlocks at the current cycle.
@ -892,14 +675,12 @@ private:
// Is the scheduled region resource limited vs. latency limited.
bool IsResourceLimited;
public:
private:
/// Record how resources have been allocated across the cycles of
/// the execution.
std::map<unsigned, ResourceSegments> ReservedResourceSegments;
std::vector<unsigned> ReservedCycles;
/// For each PIdx, stores first index into ReservedResourceSegments that
/// corresponds to it.
// Record the highest cycle at which each resource has been reserved by a
// scheduled instruction.
SmallVector<unsigned, 16> ReservedCycles;
/// For each PIdx, stores first index into ReservedCycles that corresponds to
/// it.
///
/// For example, consider the following 3 resources (ResourceCount =
/// 3):
@ -915,14 +696,12 @@ private:
/// +------------+--------+
///
/// In this case, the total number of resource instances is 6. The
/// vector \ref ReservedResourceSegments will have a slot for each instance.
/// The vector \ref ReservedCyclesIndex will track at what index the first
/// vector \ref ReservedCycles will have a slot for each instance. The
/// vector \ref ReservedCyclesIndex will track at what index the first
/// instance of the resource is found in the vector of \ref
/// ReservedResourceSegments:
///
/// Indexes of instances in
/// ReservedResourceSegments
/// ReservedCycles:
///
/// Indexes of instances in ReservedCycles
/// 0 1 2 3 4 5
/// ReservedCyclesIndex[0] = 0; [X0, X1,
/// ReservedCyclesIndex[1] = 2; Y0, Y1, Y2
@ -1008,13 +787,11 @@ public:
unsigned getLatencyStallCycles(SUnit *SU);
unsigned getNextResourceCycleByInstance(unsigned InstanceIndex,
unsigned Cycles,
unsigned StartAtCycle);
unsigned Cycles);
std::pair<unsigned, unsigned> getNextResourceCycle(const MCSchedClassDesc *SC,
unsigned PIdx,
unsigned Cycles,
unsigned StartAtCycle);
unsigned Cycles);
bool isUnbufferedGroup(unsigned PIdx) const {
return SchedModel->getProcResource(PIdx)->SubUnitsIdxBegin &&
@ -1043,8 +820,7 @@ public:
void incExecutedResources(unsigned PIdx, unsigned Count);
unsigned countResource(const MCSchedClassDesc *SC, unsigned PIdx,
unsigned Cycles, unsigned ReadyCycle,
unsigned StartAtCycle);
unsigned Cycles, unsigned ReadyCycle);
void bumpNode(SUnit *SU);

View File

@ -90,7 +90,7 @@ public:
bool hasInstrSchedModelOrItineraries() const {
return hasInstrSchedModel() || hasInstrItineraries();
}
bool enableIntervals() const { return SchedModel.EnableIntervals; }
/// Identify the processor corresponding to the current subtarget.
unsigned getProcessorID() const { return SchedModel.getProcessorID(); }

View File

@ -58,17 +58,10 @@ struct MCProcResourceDesc {
}
};
/// Identify one of the processor resource kinds consumed by a
/// particular scheduling class for the specified number of cycles.
/// TODO: consider renaming the field `StartAtCycle` and `Cycles` to
/// `AcquireAtCycle` and `ReleaseAtCycle` respectively, to stress the
/// fact that resource allocation is now represented as an interval,
/// relatively to the issue cycle of the instruction.
/// Identify one of the processor resource kinds consumed by a particular
/// scheduling class for the specified number of cycles.
struct MCWriteProcResEntry {
uint16_t ProcResourceIdx;
/// Cycle at which the resource will be released by an instruction,
/// relatively to the cycle in which the instruction is issued
/// (assuming no stalls inbetween).
uint16_t Cycles;
/// Cycle at which the resource will be grabbed by an instruction,
/// relatively to the cycle in which the instruction is issued
@ -313,11 +306,6 @@ struct MCSchedModel {
bool CompleteModel;
// Tells the MachineScheduler whether or not to track resource usage
// using intervals via ResourceSegments (see
// llvm/include/llvm/CodeGen/MachineScheduler.h).
bool EnableIntervals;
unsigned ProcID;
const MCProcResourceDesc *ProcResourceTable;
const MCSchedClassDesc *SchedClassTable;

View File

@ -117,11 +117,6 @@ class SchedMachineModel {
list<Predicate> UnsupportedFeatures = [];
bit NoModel = false; // Special tag to indicate missing machine model.
// Tells the MachineScheduler whether or not to track resource usage
// using intervals via ResourceSegments (see
// llvm/include/llvm/CodeGen/MachineScheduler.h).
bit EnableIntervals = false;
}
def NoSchedModel : SchedMachineModel {

View File

@ -162,10 +162,6 @@ static cl::opt<unsigned>
cl::init(5));
#endif
static cl::opt<unsigned>
MIResourceCutOff("misched-resource-cutoff", cl::Hidden,
cl::desc("Number of intervals to track"), cl::init(10));
// DAG subtrees must have at least this many nodes.
static const unsigned MinSubtreeSize = 8;
@ -2172,7 +2168,6 @@ void SchedBoundary::reset() {
ZoneCritResIdx = 0;
IsResourceLimited = false;
ReservedCycles.clear();
ReservedResourceSegments.clear();
ReservedCyclesIndex.clear();
ResourceGroupSubUnitMasks.clear();
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
@ -2201,8 +2196,7 @@ init(ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel) {
PE = SchedModel->getWriteProcResEnd(SC); PI != PE; ++PI) {
unsigned PIdx = PI->ProcResourceIdx;
unsigned Factor = SchedModel->getResourceFactor(PIdx);
assert(PI->Cycles >= PI->StartAtCycle);
RemainingCounts[PIdx] += (Factor * (PI->Cycles - PI->StartAtCycle));
RemainingCounts[PIdx] += (Factor * PI->Cycles);
}
}
}
@ -2255,17 +2249,7 @@ unsigned SchedBoundary::getLatencyStallCycles(SUnit *SU) {
/// Compute the next cycle at which the given processor resource unit
/// can be scheduled.
unsigned SchedBoundary::getNextResourceCycleByInstance(unsigned InstanceIdx,
unsigned Cycles,
unsigned StartAtCycle) {
if (SchedModel && SchedModel->enableIntervals()) {
if (isTop())
return ReservedResourceSegments[InstanceIdx].getFirstAvailableAtFromTop(
CurrCycle, StartAtCycle, Cycles);
return ReservedResourceSegments[InstanceIdx].getFirstAvailableAtFromBottom(
CurrCycle, StartAtCycle, Cycles);
}
unsigned Cycles) {
unsigned NextUnreserved = ReservedCycles[InstanceIdx];
// If this resource has never been used, always return cycle zero.
if (NextUnreserved == InvalidCycle)
@ -2281,7 +2265,7 @@ unsigned SchedBoundary::getNextResourceCycleByInstance(unsigned InstanceIdx,
/// instance in the reserved cycles vector.
std::pair<unsigned, unsigned>
SchedBoundary::getNextResourceCycle(const MCSchedClassDesc *SC, unsigned PIdx,
unsigned Cycles, unsigned StartAtCycle) {
unsigned Cycles) {
unsigned MinNextUnreserved = InvalidCycle;
unsigned InstanceIdx = 0;
@ -2310,7 +2294,7 @@ SchedBoundary::getNextResourceCycle(const MCSchedClassDesc *SC, unsigned PIdx,
for (unsigned I = 0, End = NumberOfInstances; I < End; ++I) {
unsigned NextUnreserved, NextInstanceIdx;
std::tie(NextUnreserved, NextInstanceIdx) =
getNextResourceCycle(SC, SubUnits[I], Cycles, StartAtCycle);
getNextResourceCycle(SC, SubUnits[I], Cycles);
if (MinNextUnreserved > NextUnreserved) {
InstanceIdx = NextInstanceIdx;
MinNextUnreserved = NextUnreserved;
@ -2321,8 +2305,7 @@ SchedBoundary::getNextResourceCycle(const MCSchedClassDesc *SC, unsigned PIdx,
for (unsigned I = StartIndex, End = StartIndex + NumberOfInstances; I < End;
++I) {
unsigned NextUnreserved =
getNextResourceCycleByInstance(I, Cycles, StartAtCycle);
unsigned NextUnreserved = getNextResourceCycleByInstance(I, Cycles);
if (MinNextUnreserved > NextUnreserved) {
InstanceIdx = I;
MinNextUnreserved = NextUnreserved;
@ -2372,10 +2355,8 @@ bool SchedBoundary::checkHazard(SUnit *SU) {
SchedModel->getWriteProcResEnd(SC))) {
unsigned ResIdx = PE.ProcResourceIdx;
unsigned Cycles = PE.Cycles;
unsigned StartAtCycle = PE.StartAtCycle;
unsigned NRCycle, InstanceIdx;
std::tie(NRCycle, InstanceIdx) =
getNextResourceCycle(SC, ResIdx, Cycles, StartAtCycle);
std::tie(NRCycle, InstanceIdx) = getNextResourceCycle(SC, ResIdx, Cycles);
if (NRCycle > CurrCycle) {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
MaxObservedStall = std::max(Cycles, MaxObservedStall);
@ -2526,10 +2507,9 @@ void SchedBoundary::incExecutedResources(unsigned PIdx, unsigned Count) {
/// \return the next cycle at which the instruction may execute without
/// oversubscribing resources.
unsigned SchedBoundary::countResource(const MCSchedClassDesc *SC, unsigned PIdx,
unsigned Cycles, unsigned NextCycle,
unsigned StartAtCycle) {
unsigned Cycles, unsigned NextCycle) {
unsigned Factor = SchedModel->getResourceFactor(PIdx);
unsigned Count = Factor * (Cycles - StartAtCycle);
unsigned Count = Factor * Cycles;
LLVM_DEBUG(dbgs() << " " << SchedModel->getResourceName(PIdx) << " +"
<< Cycles << "x" << Factor << "u\n");
@ -2549,8 +2529,7 @@ unsigned SchedBoundary::countResource(const MCSchedClassDesc *SC, unsigned PIdx,
}
// For reserved resources, record the highest cycle using the resource.
unsigned NextAvailable, InstanceIdx;
std::tie(NextAvailable, InstanceIdx) =
getNextResourceCycle(SC, PIdx, Cycles, StartAtCycle);
std::tie(NextAvailable, InstanceIdx) = getNextResourceCycle(SC, PIdx, Cycles);
if (NextAvailable > CurrCycle) {
LLVM_DEBUG(dbgs() << " Resource conflict: "
<< SchedModel->getResourceName(PIdx)
@ -2629,8 +2608,8 @@ void SchedBoundary::bumpNode(SUnit *SU) {
for (TargetSchedModel::ProcResIter
PI = SchedModel->getWriteProcResBegin(SC),
PE = SchedModel->getWriteProcResEnd(SC); PI != PE; ++PI) {
unsigned RCycle = countResource(SC, PI->ProcResourceIdx, PI->Cycles,
NextCycle, PI->StartAtCycle);
unsigned RCycle =
countResource(SC, PI->ProcResourceIdx, PI->Cycles, NextCycle);
if (RCycle > NextCycle)
NextCycle = RCycle;
}
@ -2644,33 +2623,14 @@ void SchedBoundary::bumpNode(SUnit *SU) {
PE = SchedModel->getWriteProcResEnd(SC); PI != PE; ++PI) {
unsigned PIdx = PI->ProcResourceIdx;
if (SchedModel->getProcResource(PIdx)->BufferSize == 0) {
if (SchedModel && SchedModel->enableIntervals()) {
unsigned ReservedUntil, InstanceIdx;
std::tie(ReservedUntil, InstanceIdx) =
getNextResourceCycle(SC, PIdx, PI->Cycles, PI->StartAtCycle);
if (isTop()) {
ReservedResourceSegments[InstanceIdx].add(
ResourceSegments::getResourceIntervalTop(
NextCycle, PI->StartAtCycle, PI->Cycles),
MIResourceCutOff);
} else {
ReservedResourceSegments[InstanceIdx].add(
ResourceSegments::getResourceIntervalBottom(
NextCycle, PI->StartAtCycle, PI->Cycles),
MIResourceCutOff);
}
} else {
unsigned ReservedUntil, InstanceIdx;
std::tie(ReservedUntil, InstanceIdx) =
getNextResourceCycle(SC, PIdx, 0, PI->StartAtCycle);
if (isTop()) {
ReservedCycles[InstanceIdx] =
std::max(ReservedUntil, NextCycle + PI->Cycles);
} else
ReservedCycles[InstanceIdx] = NextCycle;
}
unsigned ReservedUntil, InstanceIdx;
std::tie(ReservedUntil, InstanceIdx) =
getNextResourceCycle(SC, PIdx, 0);
if (isTop()) {
ReservedCycles[InstanceIdx] =
std::max(ReservedUntil, NextCycle + PI->Cycles);
} else
ReservedCycles[InstanceIdx] = NextCycle;
}
}
}
@ -2810,14 +2770,8 @@ LLVM_DUMP_METHOD void SchedBoundary::dumpReservedCycles() const {
const unsigned NumUnits = SchedModel->getProcResource(ResIdx)->NumUnits;
std::string ResName = SchedModel->getResourceName(ResIdx);
for (unsigned UnitIdx = 0; UnitIdx < NumUnits; ++UnitIdx) {
dbgs() << ResName << "(" << UnitIdx << ") = ";
if (SchedModel && SchedModel->enableIntervals()) {
if (ReservedResourceSegments.count(StartIdx + UnitIdx))
dbgs() << ReservedResourceSegments.at(StartIdx + UnitIdx);
else
dbgs() << "{ }\n";
} else
dbgs() << ReservedCycles[StartIdx + UnitIdx] << "\n";
dbgs() << ResName << "(" << UnitIdx
<< ") = " << ReservedCycles[StartIdx + UnitIdx] << "\n";
}
StartIdx += NumUnits;
}
@ -4184,102 +4138,3 @@ void ScheduleDAGMI::viewGraph(const Twine &Name, const Twine &Title) {
void ScheduleDAGMI::viewGraph() {
viewGraph(getDAGName(), "Scheduling-Units Graph for " + getDAGName());
}
/// Sort predicate for the intervals stored in an instance of
/// ResourceSegments. Intervals are always disjoint (no intersection
/// for any pairs of intervals), therefore we can sort the totality of
/// the intervals by looking only at the left boundary.
static bool sortIntervals(const ResourceSegments::IntervalTy &A,
const ResourceSegments::IntervalTy &B) {
return A.first < B.first;
}
unsigned ResourceSegments::getFirstAvailableAt(
unsigned CurrCycle, unsigned StartAtCycle, unsigned Cycle,
std::function<ResourceSegments::IntervalTy(unsigned, unsigned, unsigned)>
IntervalBuilder) const {
assert(std::is_sorted(std::begin(_Intervals), std::end(_Intervals),
sortIntervals) &&
"Cannot execute on an un-sorted set of intervals.");
unsigned RetCycle = CurrCycle;
ResourceSegments::IntervalTy NewInterval =
IntervalBuilder(RetCycle, StartAtCycle, Cycle);
for (auto &Interval : _Intervals) {
if (!intersects(NewInterval, Interval))
continue;
// Move the interval right next to the top of the one it
// intersects.
assert(Interval.second > NewInterval.first &&
"Invalid intervals configuration.");
RetCycle += (unsigned)Interval.second - (unsigned)NewInterval.first;
NewInterval = IntervalBuilder(RetCycle, StartAtCycle, Cycle);
}
return RetCycle;
}
void ResourceSegments::add(ResourceSegments::IntervalTy A,
const unsigned CutOff) {
using IntervalTy = ResourceSegments::IntervalTy;
assert(A.first < A.second && "Cannot add empty resource usage");
assert(CutOff > 0 && "0-size interval history has no use.");
assert(all_of(_Intervals,
[&A](const IntervalTy &Interval) -> bool {
return !intersects(A, Interval);
}) &&
"A resource is being overwritten");
_Intervals.push_back(A);
sortAndMerge();
// Do not keep the full history of the intervals, just the
// latest #CutOff.
while (_Intervals.size() > CutOff)
_Intervals.pop_front();
}
bool ResourceSegments::intersects(ResourceSegments::IntervalTy A,
ResourceSegments::IntervalTy B) {
assert(A.first <= A.second && "Invalid interval");
assert(B.first <= B.second && "Invalid interval");
// Share one boundary.
if ((A.first == B.first) || (A.second == B.second))
return true;
// full intersersect: [ *** ) B
// [***) A
if ((A.first > B.first) && (A.second < B.second))
return true;
// right intersect: [ ***) B
// [*** ) A
if ((A.first > B.first) && (A.first < B.second) && (A.second > B.second))
return true;
// left intersect: [*** ) B
// [ ***) A
if ((A.first < B.first) && (B.first < A.second) && (B.second > B.first))
return true;
return false;
}
void ResourceSegments::sortAndMerge() {
if (_Intervals.size() <= 1)
return;
// First sort the collection.
_Intervals.sort(sortIntervals);
// can use next because I have at least 2 elements in the list
auto next = std::next(std::begin(_Intervals));
auto E = std::end(_Intervals);
for (; next != E; ++next) {
if (std::prev(next)->second >= next->first) {
next->first = std::prev(next)->first;
_Intervals.erase(std::prev(next));
continue;
}
}
}

View File

@ -30,7 +30,6 @@ const MCSchedModel MCSchedModel::Default = {DefaultIssueWidth,
DefaultMispredictPenalty,
false,
true,
false /*EnableIntervals*/,
0,
nullptr,
nullptr,

View File

@ -35,7 +35,6 @@ add_llvm_unittest(CodeGenTests
RegAllocScoreTest.cpp
PassManagerTest.cpp
ScalableVectorMVTsTest.cpp
SchedBoundary.cpp
SelectionDAGAddressAnalysisTest.cpp
TypeTraitsTest.cpp
TargetOptionsTest.cpp

View File

@ -1,398 +0,0 @@
#include "llvm/CodeGen/MachineScheduler.h"
#include "gtest/gtest.h"
using namespace llvm;
#ifndef NDEBUG
TEST(ResourceSegmentsDeath, OverwriteOnRight) {
auto X = ResourceSegments({{10, 20}});
EXPECT_DEATH(X.add({15, 30}), "A resource is being overwritten");
}
TEST(ResourceSegmentsDeath, OverwriteOnLeft) {
auto X = ResourceSegments({{10, 20}});
EXPECT_DEATH(X.add({5, 11}), "A resource is being overwritten");
;
}
TEST(ResourceSegmentsDeath, FullOverwrite) {
auto X = ResourceSegments({{10, 20}});
EXPECT_DEATH(X.add({15, 18}), "A resource is being overwritten");
}
TEST(ResourceSegmentsDeath, ZeroSizeIntervalsNotAllowed) {
auto X = ResourceSegments({{10, 20}});
EXPECT_DEATH(X.add({20, 30}, 0), "0-size interval history has no use.");
}
#endif // NDEBUG
TEST(ResourceSegments, ConsecutiveLeftNoOverlap) {
auto X = ResourceSegments({{10, 20}});
X.add({7, 9});
EXPECT_EQ(X, ResourceSegments({{7, 9}, {10, 20}}));
}
TEST(ResourceSegments, ConsecutiveLeftWithOverlap) {
auto X = ResourceSegments({{10, 20}});
X.add({7, 10});
EXPECT_EQ(X, ResourceSegments({{7, 20}}));
}
TEST(ResourceSegments, ConsecutiveRightNoOverlap) {
auto X = ResourceSegments({{10, 20}});
X.add({21, 22});
EXPECT_EQ(X, ResourceSegments({{10, 20}, {21, 22}}));
}
TEST(ResourceSegments, ConsecutiveRightWithOverlap) {
auto X = ResourceSegments({{10, 20}});
X.add({20, 22});
EXPECT_EQ(X, ResourceSegments({{10, 22}}));
}
TEST(ResourceSegments, Disjoint) {
auto X = ResourceSegments({{10, 20}});
X.add({22, 23});
EXPECT_EQ(X, ResourceSegments({{10, 20}, {22, 23}}));
}
TEST(ResourceSegments, SortAfterAdd) {
auto X = ResourceSegments({{10, 20}, {3, 4}});
X.add({6, 8});
EXPECT_EQ(X, ResourceSegments({{3, 4}, {6, 8}, {10, 20}}));
}
TEST(ResourceSegments, AddWithCutOff) {
auto X = ResourceSegments({{1, 2}, {3, 4}});
X.add({6, 8}, 2);
EXPECT_EQ(X, ResourceSegments({{3, 4}, {6, 8}}));
}
TEST(ResourceSegments, add_01) {
auto X = ResourceSegments({{10, 20}, {30, 40}});
X.add({21, 29});
EXPECT_EQ(X, ResourceSegments({{10, 20}, {21, 29}, {30, 40}}));
}
TEST(ResourceSegments, add_02) {
auto X = ResourceSegments({{10, 20}, {30, 40}});
X.add({22, 29});
EXPECT_EQ(X, ResourceSegments({{10, 20}, {22, 29}, {30, 40}}));
X.add({29, 30});
EXPECT_EQ(X, ResourceSegments({{10, 20}, {22, 40}}));
}
#ifndef NDEBUG
TEST(ResourceSegmentsDeath, add_empty) {
auto X = ResourceSegments({{10, 20}, {30, 40}});
EXPECT_DEATH(X.add({22, 22}), "Cannot add empty resource usage");
}
#endif
TEST(ResourceSegments, sort_two) {
EXPECT_EQ(ResourceSegments({{30, 40}, {10, 28}}),
ResourceSegments({{10, 28}, {30, 40}}));
}
TEST(ResourceSegments, sort_three) {
EXPECT_EQ(ResourceSegments({{30, 40}, {71, 200}, {10, 29}}),
ResourceSegments({{10, 29}, {30, 40}, {71, 200}}));
}
TEST(ResourceSegments, merge_two) {
EXPECT_EQ(ResourceSegments({{10, 33}, {30, 40}}),
ResourceSegments({{10, 40}}));
EXPECT_EQ(ResourceSegments({{10, 30}, {30, 40}}),
ResourceSegments({{10, 40}}));
// Cycle 29 is resource free, so the interval is disjoint.
EXPECT_EQ(ResourceSegments({{10, 29}, {30, 40}}),
ResourceSegments({{10, 29}, {30, 40}}));
}
TEST(ResourceSegments, merge_three) {
EXPECT_EQ(ResourceSegments({{10, 29}, {30, 40}, {71, 200}}),
ResourceSegments({{10, 29}, {30, 40}, {71, 200}}));
EXPECT_EQ(ResourceSegments({{10, 29}, {30, 40}, {41, 200}}),
ResourceSegments({{10, 29}, {30, 40}, {41, 200}}));
EXPECT_EQ(ResourceSegments({{10, 30}, {30, 40}, {40, 200}}),
ResourceSegments({{10, 200}}));
EXPECT_EQ(ResourceSegments({{10, 28}, {30, 71}, {71, 200}}),
ResourceSegments({{10, 28}, {30, 200}}));
}
////////////////////////////////////////////////////////////////////////////////
// Intersection
TEST(ResourceSegments, intersects) {
// no intersect
EXPECT_FALSE(ResourceSegments::intersects({0, 1}, {3, 4}));
EXPECT_FALSE(ResourceSegments::intersects({3, 4}, {0, 1}));
EXPECT_FALSE(ResourceSegments::intersects({0, 3}, {3, 4}));
EXPECT_FALSE(ResourceSegments::intersects({3, 4}, {0, 3}));
// Share one boundary
EXPECT_TRUE(ResourceSegments::intersects({5, 6}, {5, 10}));
EXPECT_TRUE(ResourceSegments::intersects({5, 10}, {5, 6}));
// full intersect
EXPECT_TRUE(ResourceSegments::intersects({1, 2}, {0, 3}));
EXPECT_TRUE(ResourceSegments::intersects({1, 2}, {0, 2}));
EXPECT_TRUE(ResourceSegments::intersects({0, 3}, {1, 2}));
EXPECT_TRUE(ResourceSegments::intersects({0, 2}, {1, 2}));
// right intersect
EXPECT_TRUE(ResourceSegments::intersects({2, 4}, {0, 3}));
EXPECT_TRUE(ResourceSegments::intersects({0, 3}, {2, 4}));
// left intersect
EXPECT_TRUE(ResourceSegments::intersects({2, 4}, {3, 5}));
EXPECT_TRUE(ResourceSegments::intersects({3, 5}, {2, 4}));
}
////////////////////////////////////////////////////////////////////////////////
// TOP-DOWN getFirstAvailableAt
TEST(ResourceSegments, getFirstAvailableAtFromTop_oneCycle) {
auto X = ResourceSegments({{2, 5}});
// 0 1 2 3 4 5 6 7
// Res X X X
// ...X...
EXPECT_EQ(X.getFirstAvailableAtFromTop(0, 0, 1), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(1, 0, 1), 1U);
// Skip to five when hitting cycle 2
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 0, 1), 5U);
}
TEST(ResourceSegments, getFirstAvailableAtFromTop_twoCycles) {
auto X = ResourceSegments({{4, 5}});
// 0 1 2 3 4 5 6 7
// Res X
// ...X X....
EXPECT_EQ(X.getFirstAvailableAtFromTop(0, 0, 2), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(1, 0, 2), 1U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 0, 2), 2U);
// Skip to cycle 5
EXPECT_EQ(X.getFirstAvailableAtFromTop(3, 0, 2), 5U);
}
TEST(ResourceSegments, getFirstAvailableAtFromTop_twoCycles_Shifted) {
auto X = ResourceSegments({{4, 5}});
// 0 1 2 3 4 5 6 7
// Res X
// ...c X X...
EXPECT_EQ(X.getFirstAvailableAtFromTop(0, 1, 3), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(1, 1, 3), 1U);
// Skip to cycle 4
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 1, 3), 4U);
// Stay con cycle 4
// 0 1 2 3 4 5 6 7
// Res X
// ...c X X...
EXPECT_EQ(X.getFirstAvailableAtFromTop(3, 1, 3), 4U);
//
EXPECT_EQ(X.getFirstAvailableAtFromTop(4, 1, 3), 4U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(5, 1, 3), 5U);
}
TEST(ResourceSegments, getFirstAvailableAtFromTop_twoCycles_Shifted_withGap) {
auto X = ResourceSegments({{4, 5}, {7, 9}});
// 0 1 2 3 4 5 6 7 8 9
// Res X X X
// c X X
EXPECT_EQ(X.getFirstAvailableAtFromTop(1, 1, 3), 1U);
// 0 1 2 3 4 5 6 7 8 9
// Res X X X
// c X X --> moves to 4
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 1, 3), 4U);
// 0 1 2 3 4 5 6 7 8 9
// Res X X X
// c X X --> moves to 4
EXPECT_EQ(X.getFirstAvailableAtFromTop(3, 1, 3), 4U);
// 0 1 2 3 4 5 6 7 8 9
// Res X X X
// c X X --> stays on 4
EXPECT_EQ(X.getFirstAvailableAtFromTop(4, 1, 3), 4U);
// 0 1 2 3 4 5 6 7 8 9
// Res X X X
// c X X --> skips to 8
EXPECT_EQ(X.getFirstAvailableAtFromTop(5, 1, 3), 8U);
}
TEST(ResourceSegments, getFirstAvailableAtFromTop_basic) {
auto X = ResourceSegments({{5, 10}, {30, 40}});
EXPECT_EQ(X.getFirstAvailableAtFromTop(0, 3, 4), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(1, 3, 4), 1U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 3, 4), 7U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(3, 3, 4), 7U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(4, 3, 4), 7U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(5, 3, 4), 7U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(6, 3, 4), 7U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(7, 3, 4), 7U);
// Check the empty range between the two intervals of X.
EXPECT_EQ(X.getFirstAvailableAtFromTop(15, 3, 4), 15U);
// Overlap the second interval.
EXPECT_EQ(X.getFirstAvailableAtFromTop(28, 3, 4), 37U);
}
TEST(ResourceSegments, getFirstAvailableAtFromTop_advanced) {
auto X = ResourceSegments({{3, 6}, {7, 9}, {11, 14}, {30, 33}});
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 4, 5), 2U);
EXPECT_EQ(X.getFirstAvailableAtFromTop(2, 3, 4), 3U);
// Can schedule at 7U because the interval [14, 19[ does not
// overlap any of the intervals in X.
EXPECT_EQ(X.getFirstAvailableAtFromTop(1, 7, 12), 7U);
}
////////////////////////////////////////////////////////////////////////////////
// BOTTOM-UP getFirstAvailableAt
TEST(ResourceSegments, getFirstAvailableAtFromBottom) {
// Scheduling cycles move to the left...
//
// 41 40 39 ... 31 30 29 ... 21 20 19 ... 11 10 9 8 7 6 ... 1 0
// Res X X X X X X X X
// X X X X X X
// Time (relative to instruction execution) 0 1 2 3 4 5
auto X = ResourceSegments({{10, 20}, {30, 40}});
// .. but time (instruction cycle) moves to the right. Therefore, it
// is always possible to llocate a resource to the right of 0 if 0
// is not taken, because the right side of the scheduling cycles is
// empty.
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 1), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 9), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 10), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 20), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 21), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 22), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 29), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 30), 0U);
}
TEST(ResourceSegments, getFirstAvailableAtFromBottom_01) {
auto X = ResourceSegments({{3, 7}});
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// ...X... <- one cycle resource placement
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 1), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(1, 0, 1), 1U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(2, 0, 1), 2U);
// Skip to 7
EXPECT_EQ(X.getFirstAvailableAtFromBottom(3, 0, 1), 7U);
}
TEST(ResourceSegments, getFirstAvailableAtFromBottom_02) {
auto X = ResourceSegments({{3, 7}});
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// ...X X... <- two cycles resource placement
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 2), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(1, 0, 2), 1U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(2, 0, 2), 2U);
// skip to 8
EXPECT_EQ(X.getFirstAvailableAtFromBottom(3, 0, 2), 8U);
}
TEST(ResourceSegments, getFirstAvailableAtFromBottom_02_shifted) {
auto X = ResourceSegments({{3, 7}});
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// c X X <- two cycles resource placement but shifted by 1
// 0 1 2 <- cycles relative to the execution of the
// instruction
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 1, 3), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(1, 1, 3), 1U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(2, 1, 3), 2U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(3, 1, 3), 3U);
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// c X X -> skip to 9
// 0 1 2
EXPECT_EQ(X.getFirstAvailableAtFromBottom(4, 1, 3), 9U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(5, 1, 3), 9U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(6, 1, 3), 9U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(7, 1, 3), 9U);
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// c X X <- skip to 9
// 0 1 2
EXPECT_EQ(X.getFirstAvailableAtFromBottom(8, 1, 3), 9U);
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// c X X
// 0 1 2
EXPECT_EQ(X.getFirstAvailableAtFromBottom(9, 1, 3), 9U);
// 10 9 8 7 6 5 4 3 2 1 0
// X X X X
// c X X
// 0 1 2
EXPECT_EQ(X.getFirstAvailableAtFromBottom(10, 1, 3), 10U);
}
TEST(ResourceSegments, getFirstAvailableAtFromBottom_03) {
auto X = ResourceSegments({{1, 2}, {3, 7}});
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// X X X X X
// X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 1), 0U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// X X X X X
// X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(1, 0, 1), 2U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// X X X X X
// X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(2, 0, 1), 2U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// X X X X X
// X X X X X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(2, 0, 5), 11U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(3, 0, 5), 11U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(5, 0, 5), 11U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(11, 0, 5), 11U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// X X X X X
// X X X X X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(12, 0, 5), 12U);
}
TEST(ResourceSegments, getFirstAvailableAtFromBottom_03_shifted) {
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
auto X = ResourceSegments({{-3, -1}, {1, 2}, {3, 7}, {9, 10}});
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 1, 2), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 2), 0U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
// X X X -> skip to cycle 12
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 3), 12U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
// X X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 1, 3), 1U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 1, 4), 13U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(12, 1, 4), 13U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
// c X X X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(13, 1, 4), 13U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
// X X
EXPECT_EQ(X.getFirstAvailableAtFromBottom(1, 1, 3), 1U);
// 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
// C X X 0 -> skip to cycle 9
EXPECT_EQ(X.getFirstAvailableAtFromBottom(2, 1, 3), 9U);
// 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
// X X X X X X X X
// C C X X X X X -> skip to cycle 16
EXPECT_EQ(X.getFirstAvailableAtFromBottom(3, 2, 7), 16U);
}
TEST(ResourceSegments, getFirstAvailableAtFromBottom_empty) {
// Empty resource usage can accept schediling at any cycle
auto X = ResourceSegments({});
EXPECT_EQ(X.getFirstAvailableAtFromBottom(0, 0, 1), 0U);
EXPECT_EQ(X.getFirstAvailableAtFromBottom(17, 1, 22), 17U);
}

View File

@ -1453,12 +1453,6 @@ void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) {
OS << " " << (CompleteModel ? "true" : "false") << ", // "
<< "CompleteModel\n";
bool EnableIntervals =
(PM.ModelDef ? PM.ModelDef->getValueAsBit("EnableIntervals") : false);
OS << " " << (EnableIntervals ? "true" : "false") << ", // "
<< "EnableIntervals\n";
OS << " " << PM.Index << ", // Processor ID\n";
if (PM.hasInstrSchedModel())
OS << " " << PM.ModelName << "ProcResources" << ",\n"