OpenASIP 2.2
Loading...
Searching...
No Matches
Public Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
ResourceConstraintAnalyzer Class Reference

#include <ResourceConstraintAnalyzer.hh>

Collaboration diagram for ResourceConstraintAnalyzer:
Collaboration graph

Public Member Functions

 ResourceConstraintAnalyzer (DataDependenceGraph &ddg, SimpleResourceManager &rm, TCEString graphName)
 
virtual ~ResourceConstraintAnalyzer ()
 
void analyzePreSchedule ()
 
bool analyze ()
 

Private Types

typedef std::map< std::string, int > RFCountMap
 
typedef std::map< std::string, int > OperationCountMap
 

Private Member Functions

bool analyzeRegisterAntideps (DataDependenceGraph &ddg, std::ostream &s)
 
bool analyzeMoveNode (const MoveNode &node)
 
MoveNodefindResourceConstrainedParent (MoveNode *child)
 
void optimalScheduleResourceUsage (DataDependenceGraph &ddg, TCEString graphName)
 
void memoryBoundScheduleResourceUsage (DataDependenceGraph &ddg, TCEString graphName)
 
void dumpGraphWithStats (DataDependenceGraph &ddg, TCEString dotFileName, const std::map< int, std::list< MoveNode * > > &schedule)
 

Private Attributes

int busConstrained_
 
int operationConstrained_
 
int rfWritePortConstrained_
 
int rfReadPortConstrained_
 
int minScheduleLength_
 
int unknownConstrained_
 
RFCountMap writePortConstrainedRfs_
 
RFCountMap readPortConstrainedRfs_
 
OperationCountMap operationConstrainedMoves_
 
DataDependenceGraphorigDDG_
 
SimpleResourceManagerrm_
 
std::set< MoveNode * > foundMoves_
 
TCEString graphName_
 

Detailed Description

A class that analyzes the most important resource constraints in the given scheduled basic block DDG.

Definition at line 49 of file ResourceConstraintAnalyzer.hh.

Member Typedef Documentation

◆ OperationCountMap

typedef std::map<std::string, int> ResourceConstraintAnalyzer::OperationCountMap
private

Definition at line 80 of file ResourceConstraintAnalyzer.hh.

◆ RFCountMap

typedef std::map<std::string, int> ResourceConstraintAnalyzer::RFCountMap
private

Definition at line 79 of file ResourceConstraintAnalyzer.hh.

Constructor & Destructor Documentation

◆ ResourceConstraintAnalyzer()

ResourceConstraintAnalyzer::ResourceConstraintAnalyzer ( DataDependenceGraph ddg,
SimpleResourceManager rm,
TCEString  graphName 
)
inline

Definition at line 51 of file ResourceConstraintAnalyzer.hh.

54 :
55 origDDG_(ddg), rm_(rm), graphName_(graphName) {}

◆ ~ResourceConstraintAnalyzer()

virtual ResourceConstraintAnalyzer::~ResourceConstraintAnalyzer ( )
inlinevirtual

Definition at line 56 of file ResourceConstraintAnalyzer.hh.

56{}

Member Function Documentation

◆ analyze()

bool ResourceConstraintAnalyzer::analyze ( )

Analyzes the resource constraints in the schedule.

Returns
true in case at least one resource or dep constrained move found. Thus always true in case the schedule is "perfect".

Definition at line 62 of file ResourceConstraintAnalyzer.cc.

62 {
63
66
69 criticalPath->setProgramOperationNodes(true);
71
72 optimalScheduleResourceUsage(*criticalPath, graphName_ + ".critical_path");
73 delete criticalPath; criticalPath = NULL;
74
76 memDeps->setProgramOperationNodes(true);
77 memDeps->writeToDotFile(graphName_ + ".mem_deps.dot");
78 delete memDeps; memDeps = NULL;
79
80 return true;
81#if 0
82 // TODO: finish the more intelligent iterative search for resource bottlenecks
83
93 // only sensible basic block with 1 cycle is one with only
94 // reg moves or a store, ignore those
95 minScheduleLength_ = std::max(origDDG__.height(), 2);
97
98
99
100 const int cycleCount = ddg_.largestCycle() + 1;
101 const int nodes = ddg_.nodeCount();
102
103 /*
104
105 Collect the resource constrained moves that are lengthening the
106 schedule over the critical path length.
107
108 Picks up all moves in the instructions > critical path length and
109 "climbs" up the dependency tree and collects the first resource
110 constrained move found (we use top-down scheduling) that is
111 above the minimum schedule length.
112
113 The idea is try to "lift the resource constrained DDG tree
114 upwards" to try to bring the late moves within the minimum
115 schedule length.
116
117 What if there's no such move? If all the moves in the tree
118 are after the minimum schedule? In that case some other path is
119 limiting the schedule thus we should just ignore the move and look
120 at other moves.
121
122 Should we scan all cycles or just the first cycle after the min
123 cycle? We might add too many resources in case the bottleneck is
124 resolved at the first cycle. I guess we should scan until we find
125 *one* such move? Solving its resource constraint could lead to
126 removing a bottleneck so whenever we scan more than one move there's
127 a risk of adding extra resources that do not really contribute to the
128 schedule length as the constraint might have been solved by the
129 single move's bottleneck. This leads to many evaluation iterations but
130 I guess it's better than to waste resources. Might add some kind of
131 parameter for controlling the maximum number of constrained moves
132 to find per iteration.
133
134 */
135 int maxMovesToFind = 4;
136 foundMoves_.clear();
137 resourceConstrained_ = 0;
138 for (int cycle = minScheduleLength_;
139 cycle < cycleCount && resourceConstrained_ < maxMovesToFind; ++cycle) {
140 DataDependenceGraph::NodeSet moves = ddg_.movesAtCycle(cycle);
141 for (DataDependenceGraph::NodeSet::iterator m = moves.begin();
142 m != moves.end() && resourceConstrained_ < maxMovesToFind; ++m) {
144 if (node != NULL) {
146 << "found a schedule constraining move: "
147 << node->move().toString() << " at cycle "
148 << node->cycle() << " earliest DDG cycle "
149 << ddg_.earliestCycle(*node) << std::endl;
150 ++resourceConstrained_;
151 }
152 }
153 }
154
155 if (true || resourceConstrained_ > 0) {
156
157#if 0
158 ddg_.setEdgeWeightHeuristics(DataDependenceGraph::EWH_REAL);
159
160 // print out the instructions
161 for (int c = 0; c < cycleCount; ++c) {
162 Application::logStream() << c << ": ";
163 DataDependenceGraph::NodeSet moves = ddg_.movesAtCycle(c);
164 for (DataDependenceGraph::NodeSet::iterator m = moves.begin();
165 m != moves.end(); ++m) {
166 MoveNode& mn = **m;
167// if (!ddg_.isInCriticalPath(mn) && !mn.move().isJump())
168// continue;
170 Application::logStream() << " ";
171 }
172 Application::logStream() << std::endl;
173 }
174 ddg_.setEdgeWeightHeuristics(DataDependenceGraph::EWH_DEFAULT);
175#endif
176
177 Application::logStream() << "cycles: " << cycleCount << std::endl;
178
180 << "minimum: " << minScheduleLength_ << " (from DDG longest path)"
181 << std::endl;
182
184 << "at least "
185 << resourceConstrained_ << " of " << nodes
186 << " were resource constrained:" << std::endl;
187 if (busConstrained_ > 0) {
189 << busConstrained_ << " bus (move slot) constrained"
190 << std::endl;
191 }
192
193 if (rfReadPortConstrained_ > 0) {
195 << rfReadPortConstrained_ << " RF read port constrained ";
196 for (RFCountMap::const_iterator rfi =
198 rfi != readPortConstrainedRfs_.end(); ++rfi) {
200 << (*rfi).first << ": " << (*rfi).second << " ";
201 }
202 Application::logStream() << std::endl;
203 }
204
205 if (rfWritePortConstrained_ > 0) {
207 << std::endl
208 << rfWritePortConstrained_ << " RF write port constrained ";
209 for (RFCountMap::const_iterator rfi =
211 rfi != writePortConstrainedRfs_.end(); ++rfi) {
213 << (*rfi).first << ": " << (*rfi).second << " ";
214 }
215 Application::logStream() << std::endl;
216 }
217
218 if (operationConstrained_ > 0) {
220 << std::endl
222 << " operation (FU) constrained ";
223 for (OperationCountMap::const_iterator rfi =
225 rfi != operationConstrainedMoves_.end(); ++rfi) {
227 << (*rfi).first << ": " << (*rfi).second << " ";
228 }
229 Application::logStream() << std::endl;
230 }
231 Application::logStream() << std::endl;
232 return true;
233 }
234
235 return regAntidepsFound;
236#endif
237}
static std::ostream & logStream()
@ EWH_REAL
Height returns the minimum cycle count for the schedule given enough resources.
DataDependenceGraph * criticalPathGraph()
void setEdgeWeightHeuristics(EdgeWeightHeuristics ewh)
virtual void setProgramOperationNodes(bool flag)
DataDependenceGraph * memoryDependenceGraph()
virtual void writeToDotFile(const TCEString &fileName) const
std::set< GraphNode *, typename GraphNode::Comparator > NodeSet
Definition Graph.hh:53
int cycle() const
Definition MoveNode.cc:421
TTAProgram::Move & move()
void optimalScheduleResourceUsage(DataDependenceGraph &ddg, TCEString graphName)
MoveNode * findResourceConstrainedParent(MoveNode *child)
std::string toString() const
Definition Move.cc:436

References busConstrained_, DataDependenceGraph::criticalPathGraph(), MoveNode::cycle(), DataDependenceGraph::EWH_DEFAULT, DataDependenceGraph::EWH_REAL, findResourceConstrainedParent(), foundMoves_, graphName_, Application::logStream(), DataDependenceGraph::memoryDependenceGraph(), minScheduleLength_, MoveNode::move(), operationConstrained_, operationConstrainedMoves_, optimalScheduleResourceUsage(), origDDG_, readPortConstrainedRfs_, rfReadPortConstrained_, rfWritePortConstrained_, DataDependenceGraph::setEdgeWeightHeuristics(), DataDependenceGraph::setProgramOperationNodes(), TTAProgram::Move::toString(), unknownConstrained_, writePortConstrainedRfs_, and GraphBase< GraphNode, GraphEdge >::writeToDotFile().

Referenced by BBSchedulerController::executeDDGPass().

Here is the call graph for this function:

◆ analyzeMoveNode()

bool ResourceConstraintAnalyzer::analyzeMoveNode ( const MoveNode node)
private

Analyzes the resource constraints of a single move in the schedule.

Returns
true in case the move is resource constrained.

Definition at line 717 of file ResourceConstraintAnalyzer.cc.

717 {
718
719 // moves at cycle 0 cannot be scheduled earlier
720 if (node.cycle() == 0)
721 return false;
722
723 int ddgEarliestCycle = origDDG_.earliestCycle(node);
724 if (ddgEarliestCycle == node.cycle())
725 return false;
726
727 if (node.move().isJump()) {
729 << "earliest cycle for JUMP: " << ddgEarliestCycle << std::endl;
730 // JUMP cannot be scheduled earlier than the
731 // schedule length - delay slots, they always have a fixed position
732 // in the BB
733 return false;
734 }
735
736 // operation latency
737 // look for the triggering operand and see if it's less
738 // than the latency away
739 if (node.isSourceOperation()) {
741 MoveNode& trigger = *op.triggeringMove();
742 MoveNode& opcodeSetting = op.opcodeSettingNode();
743 int latency =
744 (dynamic_cast<TTAProgram::TerminalFUPort&>(
745 opcodeSetting.move().destination())).hwOperation()->
746 latency();
747 if (node.cycle() == trigger.cycle() + latency) {
748 return false;
749 }
750 }
751
752 const TTAMachine::Machine& targetMachine = origDDG_.machine();
753
754 // check the instruction the move should have been scheduled at,
755 // resources permitting
756 TTAProgram::Instruction* prevInstr = rm_.instruction(ddgEarliestCycle);
757
758 const int busCount = targetMachine.busNavigator().count();
759 // is it bus constrained?
760 if (prevInstr->moveCount() == busCount) {
761 // it might be true or we just managed to find a different move
762 // to fill the unused slot later during scheduling
764 return true;
765 }
766
767 // RF read port constrained?
768 if (node.move().source().isGPR()) {
769 int readsFound = 0;
770 for (int m = 0; m < prevInstr->moveCount(); ++m) {
771 TTAProgram::Move& prevMove = prevInstr->move(m);
772 if (prevMove.source().isGPR() &&
773 &node.move().source().registerFile() ==
774 &prevMove.source().registerFile()) {
775 ++readsFound;
776 }
777 }
778 if (readsFound ==
779 node.move().source().registerFile().maxReads()) {
782 node.move().source().registerFile().name()]++;
783 return true;
784 }
785 }
786
787 // RF read port constrained?
788 if (node.move().destination().isGPR()) {
789 int writesFound = 0;
790 for (int m = 0; m < prevInstr->moveCount(); ++m) {
791 TTAProgram::Move& prevMove = prevInstr->move(m);
792 if (prevMove.destination().isGPR() &&
793 &node.move().destination().registerFile() ==
794 &prevMove.destination().registerFile()) {
795 ++writesFound;
796 }
797 }
798 if (writesFound ==
799 node.move().destination().registerFile().maxWrites()) {
802 node.move().destination().registerFile().name()]++;
803 return true;
804 }
805 }
806
807 // assume rest are FU constrained somehow (might be untrue in general,
808 // but with a fully connected machine a good approximation)
809 if (node.isDestinationOperation()) {
810 // if it's a trigger node, it can be contrained by an operand move,
811 // a dependency which is not recorded by the DDG
812 bool operandConstrained = false;
813 if (node.move().isTriggering()) {
815 for (int input = 0; (input < po.inputMoveCount()) &&
816 !operandConstrained; ++input) {
817
818 const MoveNode& operandMove = po.inputMove(input);
819
820 if (&operandMove == &node)
821 continue;
822
823 if (operandMove.cycle() == node.cycle()) {
824 operandConstrained = true;
825 break;
826 }
827 }
828 }
829
830 if (!operandConstrained) {
832 TCEString opName = node.destinationOperation().operation().name();
833#if 0
835 << opName << " constrained: "
836 << node.move().toString() << std::endl;
837#endif
839 return true;
840 } else if (operandConstrained){
841 return false; // operand depedency is constrained by the trigger
842 }
843 }
844
847 << std::endl
848 << "resource set constrained move of unknown reason: "
849 << node.toString() << std::endl;
850 if (node.isSourceOperation()) {
853 << "ProgramOperation: " << op.toString() << std::endl;
854 }
856 << "earliest cycle: " << origDDG_.earliestCycle(node)
857 << std::endl;
858
859
860 for (int c = 3; c >= 0; --c) {
861 int printCycle = node.cycle() - c;
862 if (printCycle > 0) {
864 << printCycle << ": "
865 << rm_.instruction(printCycle)->toString()
866 << std::endl;
867 }
868 }
869 return true;
870}
int earliestCycle(const MoveNode &moveNode, unsigned int ii=UINT_MAX, bool ignoreRegWaRs=false, bool ignoreRegWaWs=false, bool ignoreGuards=false, bool ignoreFUDeps=false, bool ignoreSameOperationEdges=false, bool assumeBypassing=false) const
const TTAMachine::Machine & machine() const
ProgramOperation & sourceOperation() const
Definition MoveNode.cc:453
bool isDestinationOperation() const
std::string toString() const
Definition MoveNode.cc:576
bool isSourceOperation() const
Definition MoveNode.cc:168
ProgramOperation & destinationOperation(unsigned int index=0) const
virtual TCEString name() const
Definition Operation.cc:93
const Operation & operation() const
int inputMoveCount() const
MoveNode * triggeringMove() const
MoveNode & opcodeSettingNode()
std::string toString() const
MoveNode & inputMove(int index) const
virtual TTAProgram::Instruction * instruction(int cycle) const override
virtual TCEString name() const
virtual BusNavigator busNavigator() const
Definition Machine.cc:356
virtual int maxReads() const
virtual int maxWrites() const
std::string toString() const
Move & move(int i) const
Terminal & source() const
Definition Move.cc:302
bool isJump() const
Definition Move.cc:164
bool isTriggering() const
Definition Move.cc:284
Terminal & destination() const
Definition Move.cc:323
virtual bool isGPR() const
Definition Terminal.cc:107
virtual const TTAMachine::RegisterFile & registerFile() const
Definition Terminal.cc:225

References busConstrained_, TTAMachine::Machine::busNavigator(), TTAMachine::Machine::Navigator< ComponentType >::count(), MoveNode::cycle(), TTAProgram::Move::destination(), MoveNode::destinationOperation(), DataDependenceGraph::earliestCycle(), ProgramOperation::inputMove(), ProgramOperation::inputMoveCount(), SimpleResourceManager::instruction(), MoveNode::isDestinationOperation(), TTAProgram::Terminal::isGPR(), TTAProgram::Move::isJump(), MoveNode::isSourceOperation(), TTAProgram::Move::isTriggering(), Application::logStream(), DataDependenceGraph::machine(), TTAMachine::RegisterFile::maxReads(), TTAMachine::RegisterFile::maxWrites(), MoveNode::move(), TTAProgram::Instruction::move(), TTAProgram::Instruction::moveCount(), TTAMachine::Component::name(), Operation::name(), ProgramOperation::opcodeSettingNode(), ProgramOperation::operation(), operationConstrained_, operationConstrainedMoves_, origDDG_, readPortConstrainedRfs_, TTAProgram::Terminal::registerFile(), rfReadPortConstrained_, rfWritePortConstrained_, rm_, TTAProgram::Move::source(), MoveNode::sourceOperation(), TTAProgram::Instruction::toString(), TTAProgram::Move::toString(), MoveNode::toString(), ProgramOperation::toString(), ProgramOperation::triggeringMove(), unknownConstrained_, and writePortConstrainedRfs_.

Referenced by findResourceConstrainedParent().

Here is the call graph for this function:

◆ analyzePreSchedule()

void ResourceConstraintAnalyzer::analyzePreSchedule ( )

Definition at line 50 of file ResourceConstraintAnalyzer.cc.

50 {
53}
void memoryBoundScheduleResourceUsage(DataDependenceGraph &ddg, TCEString graphName)

References graphName_, memoryBoundScheduleResourceUsage(), origDDG_, and DataDependenceGraph::setProgramOperationNodes().

Referenced by BBSchedulerController::executeDDGPass().

Here is the call graph for this function:

◆ analyzeRegisterAntideps()

bool ResourceConstraintAnalyzer::analyzeRegisterAntideps ( DataDependenceGraph ddg,
std::ostream &  s 
)
private

Analyzes the register antideps in the schedule.

Returns
true in case at least one move that was constrained by a register antidep which could be avoided with additional registers or better register assignment strategy was found.

Definition at line 560 of file ResourceConstraintAnalyzer.cc.

561 {
562
563 s << "schedule length: " << ddg.largestCycle() - ddg.smallestCycle() << std::endl;
564
566 s << "DDG height: " << ddg.height() << std::endl;
568
569 DataDependenceGraph* trueDepGraph = ddg.trueDependenceGraph(false);
571 s << "DDG height without register antideps: "
572 << trueDepGraph->height() << std::endl;
573
574 trueDepGraph->writeToDotFile((boost::format("%s.tddg_noreg.dot") % graphName_).str());
575
576 DataDependenceGraph* trueDepGraph2 = ddg.trueDependenceGraph(true);
578 s << "DDG height without any antideps: "
579 << trueDepGraph2->height() << std::endl;
580
581 trueDepGraph2->writeToDotFile(
582 (boost::format("%s.tddg_noantidep_.dot") % graphName_).str());
583 delete trueDepGraph2;
584
585 DataDependenceGraph* trueDepGraph3 = ddg.trueDependenceGraph(true, true);
587 s << "DDG height without any antideps and memory deps: "
588 << trueDepGraph3->height() << std::endl;
589
590 trueDepGraph3->writeToDotFile(
591 (boost::format("%s.tddg_noantidep_nonmemdep.dot") % graphName_).str());
592
593 delete trueDepGraph3;
594
595 std::map<int, int> foundCounts;
596
597 bool restrictingEdgeFound = false;
598
599 for (int i = 0; i < ddg.nodeCount(); i++) {
600 MoveNode& tail = ddg.node(i);
601 for (int e = 0; e < ddg.outDegree(tail); ++e) {
602 DataDependenceEdge& edge = ddg.outEdge(tail,e);
603 // only consider non-loop register false deps here
604 if (!edge.isFalseDep() ||
606 edge.loopDepth() > 0)
607 continue;
608 MoveNode& head = ddg.headNode(edge);
609 // if there is a *real* dependency path from the tail to head
610 // than the given antidep then it does not help if we rename
611 // the register
612 if (trueDepGraph->hasPath(tail, head))
613 continue;
614
615 // TODO: figure out why the exception gets thrown (see trunk
616 // Bug 517) and remove the try catch
617 try {
618
620 if (tail.move().source().isGPR()) {
621 foundCounts[tail.move().source().registerFile().width()]++;
622 } else if (dynamic_cast<const TTAMachine::RegisterGuard*>(
623 &tail.move().guard().guard())) {
625 dynamic_cast<const TTAMachine::RegisterGuard*>(
626 &tail.move().guard().guard());
627 foundCounts[g->registerFile()->width()]++;
628 } else {
630 << "WARNING: not a GPR: "
631 << tail.move().toString() << std::endl;
632 }
633 } else {
634 if (tail.move().destination().isGPR()) {
635 foundCounts[tail.move().destination().registerFile().width()]++;
636 } else {
638 << "WARNING: not a GPR: "
639 << tail.move().toString() << std::endl;
640 }
641 }
642
643 s << "Limiting antidep: from "
644 << tail.toString() << " to " << head.toString() << " ("
645 << edge.toString() << ")"
646 << std::endl;
647
648
649 } catch (const Exception& e) {
650 // just skip this for now, have to take a look why
651 // there are some extra R_G_war edges, example:
652 // ERROR: Move is not predicated. Edge R_G_war:bool.0
653 // between moves: 32 -> add_sub_2.in1t.add eq_gt_gtu.out1 -> bool.0
654 if (Application::verboseLevel() > 0) {
656 << "ERROR: " << e.errorMessage() << " "
657 << "Edge " << edge.toString() << " between moves: "
658 << tail.move().toString() << " "
659 << head.move().toString() << std::endl;
660 }
661 continue;
662
663 }
664 }
665 }
666 for (std::map<int, int>::iterator i = foundCounts.begin();
667 i != foundCounts.end(); ++i) {
668 int foundCount = (*i).second;
669 int bitWidth = (*i).first;
670 s << "found " << foundCount << " " << bitWidth << " bit "
671 << " register antideps that restrict parallelism" << std::endl;
672
673 }
674 delete trueDepGraph;
675 return restrictingEdgeFound;
676}
static int verboseLevel()
virtual Node & headNode(const Edge &edge) const
int nodeCount() const
virtual Edge & outEdge(const Node &node, const int index) const
Node & node(const int index) const
bool hasPath(GraphNode &src, const GraphNode &dest) const
virtual int outDegree(const Node &node) const
int height() const
TCEString toString() const
DependenceType dependenceType() const
EdgeReason edgeReason() const
DataDependenceGraph * trueDependenceGraph(bool removeMemAntideps=true, bool ignoreMemDeps=false)
std::string errorMessage() const
Definition Exception.cc:123
virtual int width() const
const RegisterFile * registerFile() const
const TTAMachine::Guard & guard() const
Definition MoveGuard.cc:86
MoveGuard & guard() const
Definition Move.cc:345

References DataDependenceEdge::DEP_WAR, DataDependenceEdge::dependenceType(), TTAProgram::Move::destination(), DataDependenceEdge::EDGE_REGISTER, DataDependenceEdge::edgeReason(), Exception::errorMessage(), DataDependenceGraph::EWH_DEFAULT, DataDependenceGraph::EWH_REAL, graphName_, TTAProgram::Move::guard(), TTAProgram::MoveGuard::guard(), BoostGraph< GraphNode, GraphEdge >::hasPath(), BoostGraph< GraphNode, GraphEdge >::headNode(), BoostGraph< GraphNode, GraphEdge >::height(), DataDependenceEdge::isFalseDep(), TTAProgram::Terminal::isGPR(), DataDependenceGraph::largestCycle(), Application::logStream(), DataDependenceEdge::loopDepth(), MoveNode::move(), BoostGraph< GraphNode, GraphEdge >::node(), BoostGraph< GraphNode, GraphEdge >::nodeCount(), BoostGraph< GraphNode, GraphEdge >::outDegree(), BoostGraph< GraphNode, GraphEdge >::outEdge(), TTAMachine::RegisterGuard::registerFile(), TTAProgram::Terminal::registerFile(), DataDependenceGraph::setEdgeWeightHeuristics(), DataDependenceGraph::smallestCycle(), TTAProgram::Move::source(), DataDependenceEdge::toString(), TTAProgram::Move::toString(), MoveNode::toString(), DataDependenceGraph::trueDependenceGraph(), Application::verboseLevel(), TTAMachine::BaseRegisterFile::width(), and GraphBase< GraphNode, GraphEdge >::writeToDotFile().

Referenced by dumpGraphWithStats().

Here is the call graph for this function:

◆ dumpGraphWithStats()

void ResourceConstraintAnalyzer::dumpGraphWithStats ( DataDependenceGraph ddg,
TCEString  dotFileName,
const std::map< int, std::list< MoveNode * > > &  schedule 
)
private

Definition at line 241 of file ResourceConstraintAnalyzer.cc.

243 {
244
245 std::ofstream s(dotFileName.c_str());
246
247 s << "digraph ddg {" << std::endl;
248
249
250 s << "/*" << std::endl << "### dependence constraints: " << std::endl << std::endl;
251
253
254 int smallestCycle = (*schedule.begin()).first;
255 int largestCycle = (*schedule.rbegin()).first;
256
257 // record stats of operationA -> operationB bypasses to guide
258 // IC customization
259 typedef std::map<std::pair<TCEString, TCEString>, int> BypassMap;
260
261 typedef std::map<TCEString, int> OpCountMap;
262 OpCountMap maxParallelOps;
263 // The operation mix. I.e., the static occurence of operations
264 // in the code.
265 OpCountMap operationMix;
266 BypassMap bypasses;
267
268 /* In the critical path DDGs, the trigger nodes might be
269 missing. In that case we use the operand node to record
270 the operation usage cycle. This set is used to ensure the
271 same operation is not recorded more than once. */
272 std::set<ProgramOperation*> recordedOps;
273
274 for (int c = smallestCycle; c <= largestCycle; ++c) {
275 if (schedule.find(c) == schedule.end()) continue;
276 std::list<MoveNode*> moves = (*schedule.find(c)).second;
277 if (moves.size() == 0) continue;
278
279 std::map<TCEString, int> parallelOpsAtCycle;
280 for (std::list<MoveNode*>::iterator i = moves.begin();
281 i != moves.end(); ++i) {
282 MoveNode& n = **i;
283 TCEString opName = "";
284
285 if (n.isBypass()) {
286 std::pair<TCEString, TCEString> key =
287 std::make_pair(
290 bypasses[key]++;
291 }
292
293 if (n.isDestinationOperation()) {
295 if (!AssocTools::containsKey(recordedOps, &op)) {
296 opName = op.operation().name();
297 operationMix[opName]++;
298 parallelOpsAtCycle[opName]++;
299 recordedOps.insert(&op);
300 }
301 }
302 }
303
304 for (OpCountMap::const_iterator i = parallelOpsAtCycle.begin();
305 i != parallelOpsAtCycle.end(); ++i) {
306 TCEString opName = (*i).first;
307 int count = (*i).second;
308 maxParallelOps[opName] =
309 std::max(maxParallelOps[opName], count);
310 }
311 }
312
313 s << std::endl;
314 s << "### resource statistics: " << std::endl << std::endl;
315
316 const int COL_WIDTH = 14;
317 // print statistics of the graph as a comment
318 s << std::setw(COL_WIDTH) << std::right << "operation stats: ";
319 s << std::endl << std::endl;
320
321 for (OpCountMap::const_iterator i = maxParallelOps.begin();
322 i != maxParallelOps.end(); ++i) {
323 TCEString opName = (*i).first;
324 int parCount = (*i).second;
325 int total = operationMix[opName];
326 s << std::setw(COL_WIDTH) << std::right
327 << opName + ": ";
328 s << std::setw(COL_WIDTH) << std::right
329 << total;
330 s << " total, " << std::setw(COL_WIDTH) << std::right
331 << parCount << " at most in parallel" << std::endl;
332 }
333
334 s << std::endl;
335 // print statistics of the graph as a comment
336 s << std::setw(COL_WIDTH) << std::right << "bypass stats: ";
337 s << std::endl << std::endl;
338
339 for (BypassMap::const_iterator i = bypasses.begin();
340 i != bypasses.end(); ++i) {
341 std::pair<TCEString, TCEString> opPair = (*i).first;
342 int count = (*i).second;
343 s << std::setw(COL_WIDTH) << std::right
344 << opPair.first + " -> " + opPair.second + ": ";
345 s << std::setw(COL_WIDTH) << std::right
346 << count << std::endl;
347 }
348
350
351 s << std::endl << "### moves not at the earliest cycles:" << std::endl << std::endl;
352 for (int i = 0; i < ddg.nodeCount(); ++i) {
353 MoveNode& n = ddg.node(i);
354 // Constant writes always report 0 for the src distance.
355 if (n.isSourceConstant()) continue;
356 int ddgEarliest = ddg.maxSourceDistance(n);
357 if (n.cycle() > ddgEarliest)
358 s << n.toString() << " (" << n.cycle() << " > " << ddgEarliest << ")" << std::endl;
359 }
361
362 s << std::endl << "*/" << std::endl << std::endl;
363
364 // print the DDG itself
365
366 // print the "time line" to visualize the schedule
367 s << "\t{" << std::endl
368 << "\t\tnode [shape=plaintext];" << std::endl
369 << "\t\t";
370 for (int c = smallestCycle; c <= largestCycle; ++c) {
371 s << "\"cycle " << c << "\" -> ";
372 }
373 s << "\"cycle " << largestCycle + 1 << "\"; "
374 << std::endl << "\t}" << std::endl;
375
376 // print the nodes
377 for (int c = smallestCycle; c <= largestCycle; ++c) {
378 if (schedule.find(c) == schedule.end()) continue;
379 std::list<MoveNode*> moves = (*schedule.find(c)).second;
380 if (moves.size() == 0) continue;
381 s << "\t{ rank = same; \"cycle " << c << "\"; ";
382 for (std::list<MoveNode*>::iterator i = moves.begin();
383 i != moves.end(); ++i) {
384 MoveNode& n = **i;
385 s << "n" << n.nodeID() << "; ";
386 }
387 s << "}" << std::endl;
388 }
389
390 // first print all the nodes and their properties
391 for (int i = 0; i < ddg.nodeCount(); ++i) {
392 MoveNode& n = ddg.node(i);
393 s << "\tn" << n.nodeID()
394 << " [" << n.dotString() << "]; "
395 << std::endl;
396 }
397
398 // edges
399 for (int count = ddg.edgeCount(), i = 0; i < count ; ++i) {
400 DataDependenceEdge& e = ddg.edge(i);
401 MoveNode& tail = ddg.tailNode(e);
402 MoveNode& head = ddg.headNode(e);
403
404 s << "\tn" << tail.nodeID() << " -> n"
405 << head.nodeID() << "["
406 << e.dotString() << "];" << std::endl;
407 }
408
409 s << "}" << std::endl;
410
411 s.close();
412}
static bool containsKey(const ContainerType &aContainer, const KeyType &aKey)
int maxSourceDistance(const GraphNode &node) const
int edgeCount() const
virtual Node & tailNode(const Edge &edge) const
virtual Edge & edge(const int index) const
virtual TCEString dotString() const
Definition GraphEdge.cc:81
int nodeID() const
std::string dotString() const
Definition MoveNode.cc:602
bool isBypass() const
Definition MoveNode.cc:280
bool isSourceConstant() const
Definition MoveNode.cc:238
bool analyzeRegisterAntideps(DataDependenceGraph &ddg, std::ostream &s)

References analyzeRegisterAntideps(), AssocTools::containsKey(), MoveNode::cycle(), MoveNode::destinationOperation(), GraphEdge::dotString(), MoveNode::dotString(), BoostGraph< GraphNode, GraphEdge >::edge(), BoostGraph< GraphNode, GraphEdge >::edgeCount(), DataDependenceGraph::EWH_DEFAULT, DataDependenceGraph::EWH_REAL, BoostGraph< GraphNode, GraphEdge >::headNode(), MoveNode::isBypass(), MoveNode::isDestinationOperation(), MoveNode::isSourceConstant(), BoostGraph< GraphNode, GraphEdge >::maxSourceDistance(), Operation::name(), BoostGraph< GraphNode, GraphEdge >::node(), BoostGraph< GraphNode, GraphEdge >::nodeCount(), GraphNode::nodeID(), ProgramOperation::operation(), DataDependenceGraph::setEdgeWeightHeuristics(), MoveNode::sourceOperation(), BoostGraph< GraphNode, GraphEdge >::tailNode(), and MoveNode::toString().

Referenced by memoryBoundScheduleResourceUsage(), and optimalScheduleResourceUsage().

Here is the call graph for this function:

◆ findResourceConstrainedParent()

MoveNode * ResourceConstraintAnalyzer::findResourceConstrainedParent ( MoveNode child)
private

Finds a move that is a (grand)parent of the given move (or the move itself) and constraints the schedule, that is, could be scheduled before the minimum schedule length in case a resource constraint was removed.

A recursive function.

Returns
a found parent or NULL if not found in that subtree.

Definition at line 688 of file ResourceConstraintAnalyzer.cc.

688 {
689
690
691 if (child == NULL || !child->isMove())
692 return NULL;
693
694 int ddgEarliestCycle = origDDG_.earliestCycle(*child);
695 if (ddgEarliestCycle < minScheduleLength_ &&
696 foundMoves_.find(child) == foundMoves_.end() &&
697 analyzeMoveNode(*child)) {
698 return child;
699 } else {
701 for (DataDependenceGraph::NodeSet::const_iterator p = parents.begin();
702 p != parents.end(); ++p) {
703 MoveNode* mn = *p;
704 if (findResourceConstrainedParent(mn) != NULL)
705 return mn;
706 }
707 }
708 return NULL;
709}
virtual NodeSet predecessors(const Node &node, bool ignoreBackEdges=false, bool ignoreForwardEdges=false) const
bool isMove() const
bool analyzeMoveNode(const MoveNode &node)

References analyzeMoveNode(), DataDependenceGraph::earliestCycle(), findResourceConstrainedParent(), foundMoves_, MoveNode::isMove(), minScheduleLength_, origDDG_, and BoostGraph< GraphNode, GraphEdge >::predecessors().

Referenced by analyze(), and findResourceConstrainedParent().

Here is the call graph for this function:

◆ memoryBoundScheduleResourceUsage()

void ResourceConstraintAnalyzer::memoryBoundScheduleResourceUsage ( DataDependenceGraph ddg,
TCEString  graphName 
)
private

Analyses the program based on a memory resource constrained schedule.

Produces statistics of maximum number of parallel operations that can be potentially utilized given that there's only a fixed number of parallel memory operations allowed.

Definition at line 466 of file ResourceConstraintAnalyzer.cc.

467 {
468
469 TCEString dotFileName = graphName + ".memory_bound_optimal_schedule.dot";
470
471 int maxMemOpsPerCycle = 2;
472 std::map<int, std::list<MoveNode*> > schedule;
473 std::map<int, std::list<ProgramOperationPtr> > operationSchedule;
474
475 // Maximum number of parallel operations of the given name.
476 std::map<TCEString, unsigned> maxParallelOps;
477
479
480 CriticalPathBBMoveNodeSelector selector(ddg, ddg.machine());
481
483 << "### analyzing " << graphName << std::endl;
484
485 // Produce the "operation schedule" where we assume there are enough
486 // interconnection and RF port resources to invoke the required operations
487 // in parallel.
488 MoveNodeGroup cands = selector.candidates();
489
490 while (cands.nodeCount() > 0) {
491 if (cands.nodeCount() > 1) {
493 const Operation& op = po->operation();
494 // Schedule the triggering move first and at the earliest position,
495 // rest of the nodes can fall in to cycles based on the DDG. Assume
496 // operand 1 is always the triggering.
497 for (auto mn : po->inputNode(1)) {
498 int cycle = ddg.earliestCycle(
499 *mn, UINT_MAX, false, false, false, false, false, true);
500 int memOpsInCycle = 0;
501 do {
502 if (!op.usesMemory()) break;
503 // Assuming placing the current mem OP to the cycle.
504 memOpsInCycle = 1;
505 // Count the number of memory operations already triggered
506 // on the planned cycle.
507 for (auto otherOp : operationSchedule[cycle]) {
508 if (otherOp->operation().usesMemory())
509 memOpsInCycle++;
510 }
511 if (memOpsInCycle > maxMemOpsPerCycle) {
513 << "would get " << memOpsInCycle
514 << " mem operations in cycle " << cycle
515 << std::endl;
516 ++cycle;
517 }
518 } while (memOpsInCycle > maxMemOpsPerCycle);
519 mn->setCycle(cycle);
520 schedule[cycle].push_back(mn);
521 operationSchedule[cycle].push_back(po);
523 << "placed operation trigger " << mn->toString() << std::endl;
524 selector.notifyScheduled(*mn);
525 }
526 }
527 for (int n = 0; n < cands.nodeCount(); ++n) {
528 MoveNode& mn = cands.node(n);
529 if (mn.isPlaced()) continue;
530 int cycle = ddg.earliestCycle(
531 mn, UINT_MAX, false, false, false, false, false, true);
532 mn.setCycle(cycle);
533 schedule[cycle].push_back(&mn);
535 << "placed mn " << mn.toString() << std::endl;
536 selector.notifyScheduled(mn);
537 }
538
539 cands = selector.candidates();
540 }
541 dumpGraphWithStats(ddg, dotFileName, schedule);
543
544 // Unschedule the nodes so the actual scheduler that might be ran
545 // after this analysis does not get confused.
546 for (auto n : ddg.scheduledMoves()) {
547 n->unsetCycle();
548 }
549}
std::shared_ptr< ProgramOperation > ProgramOperationPtr
Definition MoveNode.hh:53
ProgramOperationPtr programOperationPtr() const
int nodeCount() const
MoveNode & node(int index) const
bool isPlaced() const
Definition MoveNode.cc:352
void setCycle(const int newcycle)
Definition MoveNode.cc:503
virtual bool usesMemory() const
Definition Operation.cc:232
void dumpGraphWithStats(DataDependenceGraph &ddg, TCEString dotFileName, const std::map< int, std::list< MoveNode * > > &schedule)

References CriticalPathBBMoveNodeSelector::candidates(), dumpGraphWithStats(), DataDependenceGraph::earliestCycle(), DataDependenceGraph::EWH_DEFAULT, DataDependenceGraph::EWH_REAL, MoveNode::isPlaced(), Application::logStream(), DataDependenceGraph::machine(), MoveNodeGroup::node(), MoveNodeGroup::nodeCount(), CriticalPathBBMoveNodeSelector::notifyScheduled(), MoveNodeGroup::programOperationPtr(), DataDependenceGraph::scheduledMoves(), MoveNode::setCycle(), DataDependenceGraph::setEdgeWeightHeuristics(), MoveNode::toString(), and Operation::usesMemory().

Referenced by analyzePreSchedule().

Here is the call graph for this function:

◆ optimalScheduleResourceUsage()

void ResourceConstraintAnalyzer::optimalScheduleResourceUsage ( DataDependenceGraph ddg,
TCEString  graphName 
)
private

Analyses the program based on a non-resource constrained schedule.

Produces statistics of maximum number of parallel operations that can be potentially utilized.

Todo:
this is incomplete as the operand to trigger edges are missing, thus the analysis results are likely to be false.

Definition at line 424 of file ResourceConstraintAnalyzer.cc.

425 {
426
427 ddg.writeToDotFile(graphName + ".dot");
428
429 TCEString dotFileName = graphName + ".optimal_schedule.dot";
430
431 std::map<int, std::list<MoveNode*> > schedule;
432
434 for (int nc = 0; nc < ddg.nodeCount(); ++nc) {
435 MoveNode& n = ddg.node(nc);
436 int cycle = ddg.maxSourceDistance(n);
437 // immediate operand moves always get cycle 0, fix this by
438 // assuming the cycle is the same as the latest other operand
439 // move (at least one of them must be a non-immediate move)
440 if (n.isDestinationOperation() &&
441 n.move().source().isImmediate()) {
442 for (int input = 0;
443 input < n.destinationOperation().inputMoveCount();
444 ++input) {
445 MoveNode& inputMove =
447 if (&inputMove == &n) continue;
448 cycle = std::max(cycle, ddg.maxSourceDistance(inputMove));
449 }
450 }
451 schedule[cycle].push_back(&n);
452 }
454
455 dumpGraphWithStats(ddg, dotFileName, schedule);
456}
virtual bool isImmediate() const
Definition Terminal.cc:63

References MoveNode::destinationOperation(), dumpGraphWithStats(), DataDependenceGraph::EWH_DEFAULT, DataDependenceGraph::EWH_REAL, ProgramOperation::inputMove(), MoveNode::isDestinationOperation(), TTAProgram::Terminal::isImmediate(), BoostGraph< GraphNode, GraphEdge >::maxSourceDistance(), MoveNode::move(), BoostGraph< GraphNode, GraphEdge >::node(), BoostGraph< GraphNode, GraphEdge >::nodeCount(), DataDependenceGraph::setEdgeWeightHeuristics(), TTAProgram::Move::source(), and GraphBase< GraphNode, GraphEdge >::writeToDotFile().

Referenced by analyze().

Here is the call graph for this function:

Member Data Documentation

◆ busConstrained_

int ResourceConstraintAnalyzer::busConstrained_
private

Definition at line 82 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ foundMoves_

std::set<MoveNode*> ResourceConstraintAnalyzer::foundMoves_
private

Definition at line 97 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and findResourceConstrainedParent().

◆ graphName_

TCEString ResourceConstraintAnalyzer::graphName_
private

◆ minScheduleLength_

int ResourceConstraintAnalyzer::minScheduleLength_
private

Definition at line 86 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and findResourceConstrainedParent().

◆ operationConstrained_

int ResourceConstraintAnalyzer::operationConstrained_
private

Definition at line 83 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ operationConstrainedMoves_

OperationCountMap ResourceConstraintAnalyzer::operationConstrainedMoves_
private

Definition at line 92 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ origDDG_

DataDependenceGraph& ResourceConstraintAnalyzer::origDDG_
private

◆ readPortConstrainedRfs_

RFCountMap ResourceConstraintAnalyzer::readPortConstrainedRfs_
private

Definition at line 90 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ rfReadPortConstrained_

int ResourceConstraintAnalyzer::rfReadPortConstrained_
private

Definition at line 85 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ rfWritePortConstrained_

int ResourceConstraintAnalyzer::rfWritePortConstrained_
private

Definition at line 84 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ rm_

SimpleResourceManager& ResourceConstraintAnalyzer::rm_
private

Definition at line 95 of file ResourceConstraintAnalyzer.hh.

Referenced by analyzeMoveNode().

◆ unknownConstrained_

int ResourceConstraintAnalyzer::unknownConstrained_
private

Definition at line 87 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().

◆ writePortConstrainedRfs_

RFCountMap ResourceConstraintAnalyzer::writePortConstrainedRfs_
private

Definition at line 89 of file ResourceConstraintAnalyzer.hh.

Referenced by analyze(), and analyzeMoveNode().


The documentation for this class was generated from the following files: