OpenASIP 2.2
Loading...
Searching...
No Matches
BasicBlockScheduler.cc
Go to the documentation of this file.
1/*
2 Copyright (c) 2002-2009 Tampere University.
3
4 This file is part of TTA-Based Codesign Environment (TCE).
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 DEALINGS IN THE SOFTWARE.
23 */
24/**
25 * @file BasicBlockScheduler.cc
26 *
27 * Definition of BasicBlockScheduler class.
28 *
29 * @author Pekka Jääskeläinen 2006 (pjaaskel-no.spam-cs.tut.fi)
30 * @author Fabio Garzia 2010 (fabio.garzia-no.spam-tut.fi)
31 * @note rating: red
32 */
33
34#include <set>
35#include <string>
36#include <cstdlib>
37
42#include "POMDisassembler.hh"
43#include "ProgramOperation.hh"
44#include "ControlUnit.hh"
45#include "Machine.hh"
46#include "UniversalMachine.hh"
47#include "Guard.hh"
48#include "MapTools.hh"
49#include "Instruction.hh"
51#include "BasicBlock.hh"
52#include "RegisterCopyAdder.hh"
54#include "TCEString.hh"
55#include "Application.hh"
57#include "HWOperation.hh"
58#include "FUPort.hh"
59#include "CodeGenerator.hh"
60#include "TerminalImmediate.hh"
61#include "InterPassData.hh"
62#include "MoveNodeSet.hh"
63#include "RegisterRenamer.hh"
64#include "Operation.hh"
65#include "FUPort.hh"
66#include "HWOperation.hh"
67
68//#define DEBUG_OUTPUT
69//#define DEBUG_REG_COPY_ADDER
70
71#define RESCHEDULE_NEXT_CYCLE_AFTER_DRE
72
73
75
76/**
77 * Constructs the basic block scheduler.
78 *
79 * @param data Interpass data
80 * @param bypasser Helper module implementing software bypassing
81 * @param delaySlotFiller Helper module implementing jump delay slot filling
82 */
84 InterPassData& data,
85 SoftwareBypasser* bypasser, RegisterRenamer* renamer) :
86 DDGPass(data), BasicBlockPass(data), ddg_(NULL), rm_(NULL),
87 softwareBypasser_(bypasser), renamer_(renamer), minCycle_(0),
88 jumpNode_(NULL) {
90 options_ = dynamic_cast<LLVMTCECmdLineOptions*>(cmdLineOptions);
91}
92
95
96/**
97 * Schedules a piece of code in a DDG
98 *
99 * @param ddg The ddg containing the code
100 * @param rm Resource manager that is to be used.
101 * @param targetMachine The target machine.
102 * @return size of the produces schedule.
103 * @exception Exception several TCE exceptions can be thrown in case of
104 * a scheduling error.
105 */
106int
109 const TTAMachine::Machine& targetMachine, int minCycle, bool test) {
110 tripCount_ = 0;
111 ddg_ = &ddg;
112 targetMachine_ = &targetMachine;
113 minCycle_ = minCycle;
114
115 if (renamer_ != NULL) {
116 renamer_->initialize(ddg);
117 }
118
119 if (options_ != NULL && options_->dumpDDGsDot()) {
121 ddg, std::string("0"), DataDependenceGraph::DUMP_DOT, false);
122 }
123
124 if (options_ != NULL && options_->dumpDDGsXML()) {
126 ddg, std::string("0"), DataDependenceGraph::DUMP_XML, false);
127 }
128
129 scheduledTempMoves_.clear();
130
131 // empty DDGs can be ignored
132 if (ddg.nodeCount() == 0 ||
133 (ddg.nodeCount() == 1 && !ddg.node(0).isMove())) {
134 return 0;
135 }
136
137 CriticalPathBBMoveNodeSelector selector(ddg, targetMachine);
138 selector_ = &selector;
139
140 rm_ = &rm;
141
142
143 // register selector to bypasser for notfications.
144 if (softwareBypasser_ != NULL) {
145 softwareBypasser_->setSelector(&selector);
146 }
147
148 if (renamer_ != NULL) {
149 renamer_->setSelector(&selector);
150 }
151
152 MoveNodeGroup moves = selector.candidates();
153 while (moves.nodeCount() > 0) {
154 bool movesRemoved = false;
155 MoveNode& firstMove = moves.node(0);
156 if (firstMove.isOperationMove()) {
157 scheduleOperation(moves);
158 } else {
159 if (firstMove.move().destination().isRA()) {
160 scheduleMove(firstMove,0, true);
161 } else {
162 scheduleRRMove(firstMove);
163 int lastOperandFake = 0;
164 if (softwareBypasser_ != NULL) {
165 int rv =
167 firstMove, lastOperandFake, ddg, rm);
168 if (rv < 0) {
170 firstMove, ddg, rm, true);
171 scheduleRRMove(firstMove);
172 }
173
174 // did the bypass cause a reg-to-itself copy?
175 // if it did, let's kill it.
176 if (firstMove.move().source().equals(
177 firstMove.move().destination())) {
178 movesRemoved = true;
179 }
180
181 if (rv > 0) {
182 std::set<std::pair<TTAProgram::Move*, int> >
183 removedMoves;
185 moves, ddg, rm, removedMoves);
186 // TODO: disabled becaused may be problematic
187 // when rescheduled to different buses
188// handleRemovedResultMoves(removedMoves);
189 }
190 }
191 }
192 }
193
194 if (!movesRemoved) {
195 if (!moves.isScheduled()) {
196 std::string message = " Move(s) did not get scheduled: ";
197 for (int i = 0; i < moves.nodeCount(); i++) {
198 message += moves.node(i).toString() + " ";
199 }
200
202
203 throw ModuleRunTimeError(__FILE__,__LINE__,__func__,message);
204 }
205
206 // notifies successors of the scheduled moves.
207 notifyScheduled(moves, selector);
208 }
209
210 moves = selector.candidates();
211 }
212 int size = rm.largestCycle();
213 if (test) {
215 return size;
216 }
217 if (softwareBypasser_ != NULL) {
218 softwareBypasser_->clearCaches(ddg, true);
219 }
220
221 if (ddg.nodeCount() !=
222 ddg.scheduledNodeCount()) {
223 ddg.writeToDotFile("failed_bb.dot");
224 std::cerr << ddg.nodeCount() << ", " << ddg.scheduledNodeCount()
225 << std::endl;
226 throw ModuleRunTimeError(__FILE__, __LINE__, __func__,
227 "All moves in the DDG didn't get scheduled.");
228 }
229
230 if (options_ != NULL && options_->dumpDDGsDot()) {
231 ddgSnapshot(ddg, std::string("0"), DataDependenceGraph::DUMP_DOT, true);
232 }
233
234 if (options_ != NULL && options_->dumpDDGsXML()) {
235 ddgSnapshot(ddg, std::string("0"), DataDependenceGraph::DUMP_XML, true);
236 }
237 return size;
238}
239
240/**
241 * Schedules loop in a DDG
242 *
243 * @param ddg The ddg containing the loop
244 * @param rm Resource manager that is to be used.
245 * @param targetMachine The target machine.
246 * @exception Exception several TCE exceptions can be thrown in case of
247 * a scheduling error.
248 * @return negative if failed, else overlapcount.
249 */
250int
253 const TTAMachine::Machine& targetMachine, int tripCount,
254 SimpleResourceManager*, bool testOnly) {
255 tripCount_ = tripCount;
256 ddg_ = &ddg;
257 targetMachine_ = &targetMachine;
258 minCycle_ = 0;
259
260 if (renamer_ != NULL) {
261 renamer_->initialize(ddg);
262 }
263
264 if (options_ != NULL && options_->dumpDDGsDot()) {
266 ddg, std::string("0"), DataDependenceGraph::DUMP_DOT, false, true);
267 }
268
269 if (options_ != NULL && options_->dumpDDGsXML()) {
271 ddg, std::string("0"), DataDependenceGraph::DUMP_XML, false, true);
272 }
273
274 scheduledTempMoves_.clear();
275
276 // empty DDGs can be ignored
277 if (ddg.nodeCount() == 0 ||
278 (ddg.nodeCount() == 1 && !ddg.node(0).isMove())) {
279 return 0;
280 }
281
282 CriticalPathBBMoveNodeSelector selector(ddg, targetMachine);
283
284 rm_ = &rm;
285
286 // register selector to bypasser for notfications.
287 if (softwareBypasser_ != NULL) {
288 softwareBypasser_->setSelector(&selector);
289 }
290
291 if (renamer_ != NULL) {
292 renamer_->setSelector(&selector);
293 }
294
295 for (int i = ddg.nodeCount()-1; i>= 0 ; i--) {
296 MoveNode& node = ddg.node(i);
297 if (node.move().isControlFlowMove()) {
298 jumpNode_ = &node;
299 MoveNodeGroup mng;
300 mng.addNode(node);
302 }
303 }
304
305 MoveNodeGroup moves = selector.candidates();
306 while (moves.nodeCount() > 0) {
307
308 MoveNode& firstMove = moves.node(0);
309 if (firstMove.isOperationMove()) {
310 scheduleOperation(moves);
311 } else {
312 if (firstMove.move().destination().isRA()) {
313 scheduleMove(firstMove,0, true);
314 } else {
315 scheduleRRMove(firstMove);
316 }
317 }
318
319 if (!moves.isScheduled()) {
321 std::string("ii_") +
323 std::string("_dag.dot"));
324
326
327 return -1;
328 }
329
330 // notifies successors of the scheduled moves.
331 notifyScheduled(moves, selector);
332
333 moves = selector.candidates();
334 }
335
336 if (ddg.nodeCount() !=
337 ddg.scheduledNodeCount()) {
338 debugLog("All moves in the DDG didn't get scheduled.");
339// debugLog("Disassembly of the situation:");
340// Application::logStream() << bb.disassemble() << std::endl;
341 ddg.writeToDotFile("failed_bb.dot");
342 abortWithError("Should not happen!");
343 }
344
345 // loop schedulign did not help.
346 if (static_cast<unsigned>(ddg.largestCycle()) < rm.initiationInterval()
347 || testOnly) {
348 if (static_cast<unsigned>(ddg.largestCycle()) <
349 rm.initiationInterval()) {
351 << "No overlapping instructions."
352 << "Should Revert to ordinary scheduler."
353 << std::endl;
354 }
355 // this have to be calculated before unscheduling.
356 int rv = ddg.largestCycle() / rm.initiationInterval();
358 return rv;
359 }
360
361 // test that ext-cond jump not in prolog (where it is skipped)
362 int overlap_count = ddg.largestCycle() / rm.initiationInterval();
363 if (overlap_count >= tripCount) {
365 return -1;
366 }
367
368 if (softwareBypasser_ != NULL) {
369 softwareBypasser_->clearCaches(ddg, true);
370 }
371
372 if (options_ != NULL && options_->dumpDDGsDot()) {
373 ddgSnapshot(ddg, std::string("0"), DataDependenceGraph::DUMP_DOT, true);
374 }
375
376 if (options_ != NULL && options_->dumpDDGsXML()) {
377 ddgSnapshot(ddg, std::string("0"), DataDependenceGraph::DUMP_XML, true);
378 }
379
380 return ddg.largestCycle() / rm.initiationInterval();
381}
382
383#ifdef DEBUG_REG_COPY_ADDER
384static int graphCount = 0;
385#endif
386
387/**
388 * Schedules moves in a single operation execution.
389 *
390 * Assumes the given MoveNodeGroup contains all moves in the operation
391 * execution. Also assumes that all inputs to the MoveNodeGroup have
392 * been scheduled.
393 *
394 * @param moves Moves of the operation execution.
395 */
396void
398 ProgramOperation& po =
399 (moves.node(0).isSourceOperation())?
400 (moves.node(0).sourceOperation()):
401 (moves.node(0).destinationOperation());
402
403 // may switch operands of commutative operations. Do it before
404 // regcopyadder because it's easier here.
405 // cooperation with regcopyadder might make it perform better though.
407
408#ifdef DEBUG_REG_COPY_ADDER
409 ddg_->setCycleGrouping(true);
411 (boost::format("%s_before_ddg.dot") % ddg_->name()).str());
412#endif
413
414 RegisterCopyAdder regCopyAdder(
417 regCopyAdder.addMinimumRegisterCopies(po, *targetMachine_, ddg_);
418#ifdef DEBUG_REG_COPY_ADDER
419 const int tempsAdded = copies.count_;
420#endif
421
422#ifdef DEBUG_REG_COPY_ADDER
423 if (tempsAdded > 0) {
425 (boost::format("%s_after_regcopy_ddg.dot") % ddg_->name()).str());
426 }
427 ddg_->sanityCheck();
428#endif
429
430 MoveNodeSet tempSet;
431 for (int i = 0; i < moves.nodeCount(); i++) {
432 // MoveNodeGroup relates to DDG so we copy it to
433 // a simpler MoveNodeSet container
434 tempSet.addMoveNode(moves.node(i));
435 }
436
437 bool operandsFailed = true;
438 bool resultsFailed = true;
439 int operandsStartCycle = 0;
440
441 int maxFromRm = rm_->largestCycle();
442
443 // Magic number defining how many times we should try to schedule
444 // operands and results. Should not be needed in final version of
445 // scheduler
446 const int retryCount = 20;
447 int minOperand = operandsStartCycle;
448
449 bool tryBypassing = softwareBypasser_ != NULL;
450 bool bypassTrigger = true;
451
452 while ((operandsFailed || resultsFailed) &&
453 operandsStartCycle < maxFromRm + retryCount) {
454
455 minOperand = scheduleOperandWrites(operandsStartCycle, moves);
456 if (minOperand != -1) {
457 operandsFailed = false;
458 } else {
459 // Scheduling some operand failed, unschedule and try later cycle
460 for (int i = 0; i < moves.nodeCount(); i++){
461 MoveNode& moveNode = moves.node(i);
462 if (moveNode.isScheduled() &&
463 moveNode.isDestinationOperation()) {
464 unschedule(moveNode);
466 }
467 }
468 operandsFailed = true;
469 operandsStartCycle++;
470 continue;
471 }
472
473 regCopyAdder.operandsScheduled(copies, *ddg_);
474
475 int bypassedMoves = -1;
476 if (tryBypassing) {
477 bypassedMoves = softwareBypasser_->bypass(
478 moves, *ddg_, *rm_, bypassTrigger);
479 if (bypassedMoves == -1){
480 // bypassing failed try again without attempt to bypass
481 // this restores the original schedule of operands
482 // and disable bypassing
483 operandsFailed = true;
484 if (bypassTrigger == false) {
485 tryBypassing = false;
486 } else {
487 bypassTrigger = false;
488 }
490 continue;
491 }
492 // bypass was success
493 }
494
495 // TODO: disabled because may cause fail if rescheduled
496 // to different bus.
497// tryToDelayOperands(moves);
498
499 if (scheduleResultReads(moves)) {
500 resultsFailed = false;
501 // Results were scheduled, we are not going to change schedule
502 // of this program operation. Lets check if some of the
503 // bypassed operands did not produce dead result moves
504 if (tryBypassing) {
505 try {
506 std::set<std::pair<TTAProgram::Move*, int> >
507 removedMoves;
509 moves, *ddg_, *rm_, removedMoves);
510 handleRemovedResultMoves(removedMoves);
511 } catch (const Exception& e) {
512 throw ModuleRunTimeError(
513 __FILE__, __LINE__, __func__, e.errorMessageStack());
514 }
515 }
516 } else {
517 // Scheduling results failed, most likely due to large distance
518 // between trigger and earliest result cycle
519 // Some other operation is overwritting result on same FU
520 for (int i = 0; i < moves.nodeCount(); i++){
521 MoveNode& moveNode = moves.node(i);
522 if (moveNode.isScheduled()) {
523 // Operands were scheduled successfully but result can
524 // not be, we unschedule everything
525 unschedule(moveNode);
526 if (moveNode.isDestinationOperation()) {
528 }
529 if (moveNode.isSourceOperation()) {
531 }
532 }
533 }
534 resultsFailed = true;
535 // If some operands were bypassed, we need to remove bypassing
536 if (tryBypassing) {
538 tryBypassing = false;
539 continue;
540 }
541 // We will try to schedule operands in later cycle
542 // taking larger strides
543 operandsStartCycle++;
544 minOperand++;
545 operandsStartCycle = std::max(minOperand, operandsStartCycle);
546 }
547 }
548 // This fails if there is "timeout" on retry count.
549 // Should not be needed in final version.
550
551 if (operandsFailed) {
553 std::string("ii_") +
555 std::string("_dag.dot"));
556
558 throw ModuleRunTimeError(
559 __FILE__, __LINE__, __func__,
560 "Operands scheduling failed for \'" + moves.toString());
561 }
562 if (resultsFailed) {
564 std::string("ii_") +
566 std::string("_dag.dot"));
567
569 throw ModuleRunTimeError(
570 __FILE__, __LINE__, __func__,
571 "Results scheduling failed for \'" + moves.toString());
572 }
573
574 if (options_ != NULL &&
576 (resultsFailed || operandsFailed)) {
577
578 //static int failedCounter = 0;
579 if (options_->dumpDDGsDot()) {
580 ddgSnapshot(*ddg_, std::string("2_failed.dot"),
582 } else {
583 ddgSnapshot(*ddg_, std::string("2_failed.xml"),
585 }
586
588 throw ModuleRunTimeError(
589 __FILE__, __LINE__, __func__,
590 (boost::format("Bad BB %s") % ddg_->name()).str());
591 }
592
593 regCopyAdder.resultsScheduled(copies, *ddg_);
594
595#ifdef DEBUG_REG_COPY_ADDER
596 if (tempsAdded > 0) {
598 (boost::format("%s_after_scheduler_ddg.dot") %
599 ddg_->name()).str());
601 << "(operation fix #" << ddg_->name() << ")" << std::endl
602 << std::endl;
603
604 ++graphCount;
605 }
606#endif
607}
608
609// #define DEBUG_SCHEDULE_OPERAND_WRITES
610
611/**
612 * Schedules operand moves of an operation execution.
613 *
614 * Assumes the given MoveNodeGroup contains all moves in the operation
615 * execution. Also assumes that all inputs to the MoveNodeGroup have
616 * been scheduled. Exception to this are the possible temporary register
617 * copies inserted before the operand move due to missing connectivity.
618 * If found, the temp moves are scheduled atomically with the operand move.
619 * Assumes top-down scheduling.
620 *
621 * @param cycle Earliest cycle for starting scheduling of operands. This
622 * parameter is modified to the highest cycle one
623 * should try next in case scheduling starting from the
624 * given cycle failed.
625 * @param moves Moves of the operation execution.
626 * @return The cycle the earliest of the operands got scheduled
627 */
628int
630 const int MAX_OPERAND_CYCLE_DIFFERENCE = 15;
631
632 const int MAX_OPERATION_START_BEFORE_EARLIEST_READ = 50;
633
634 int lastOperandCycle = 0;
635 int earliestScheduledOperand = INT_MAX;
636 int startCycle = 0;
637 MoveNode* trigger = NULL;
638 MoveNode* firstToSchedule = NULL;
639
640 // find the movenode which has highest DDG->earliestCycle and limit
641 // the starting cycle of all operands close to that.
642 // this should prevent trying to schedule immediate writes very early
643 // and having lots of retries until the cycle has risen to the level of
644 // other moves.
645 // this might make SCHEDULING_WINDOW or SimpleBrokerDirector unnecessary /
646 // allow groving it much smaller without scheduler slowdown.
647 for (int i = 0; i < moves.nodeCount(); i++) {
648 int limit = 0;
649 if (moves.node(i).isDestinationOperation()) {
650 limit = ddg_->earliestCycle(
651 moves.node(i), rm_->initiationInterval()) -
652 MAX_OPERAND_CYCLE_DIFFERENCE;
653 } else if (moves.node(i).isSourceOperation()) {
654 limit = ddg_->earliestCycle(
655 moves.node(i), rm_->initiationInterval()) -
656 MAX_OPERATION_START_BEFORE_EARLIEST_READ;
657 } else {
658 continue;
659 }
660 if (limit < 0) {
661 limit = 0;
662 }
663 if (limit > cycle) {
664 cycle = limit;
665 }
666 }
667
668 // Find and schedule moveNode which has highest "earliest Cycle"
669 // other moveNodes could be scheduled in earlier cycle but this
670 // makes sure there will be no problems with operands overwritting
671 // and assignment failures. At least it reduced a count of such.
672 // Also find smallest of all earliest cycles,
673 // make no sense to start from 0
674 for (int i = 0; i < moves.nodeCount(); i++) {
675 if (!moves.node(i).isDestinationOperation()) {
676 continue;
677 }
678
679 // Temporary moves needs to be scheduled so DDG can find
680 // earliest cycle
681 // TODO: this should also have cycle limit?
682 scheduleInputOperandTempMoves(moves.node(i), moves.node(i));
683 int earliestDDG = ddg_->earliestCycle(
684 moves.node(i), rm_->initiationInterval());
685
686 if (earliestDDG == INT_MAX) {
687 ddg_->writeToDotFile("failed_bb.dot");
689 throw InvalidData(
690 __FILE__, __LINE__, __func__,
691 (boost::format("InputTempMoves failed to schedule "
692 "successfully for '%s' ") % moves.node(i).toString()).str());
693 }
694
695 // passed argument could be larger than what DDG thinks is earliest
696 earliestDDG = std::max(earliestDDG, cycle);
697 int earliest = rm_->earliestCycle(earliestDDG, moves.node(i));
698 if (earliest == -1) {
700 continue;
701 }
702 if (earliest >= startCycle) {
703 // Find first moveNode that will be scheduled
704 startCycle = earliest;
705 firstToSchedule = &moves.node(i);
706 }
708
709 // Find also smallest of earliestCycles
710 cycle = std::min(cycle, earliest);
711 }
712
713 if (firstToSchedule != NULL) {
714 // start cycle could be null if there was problem scheduling
715 // all input temp operands
716 scheduleInputOperandTempMoves(*firstToSchedule, *firstToSchedule);
717 // earliestCycle gave us the startCycle so this must pass
718 scheduleMove(*firstToSchedule, startCycle, true);
719 if (!firstToSchedule->isScheduled()) {
721 std::string("ii_") +
723 std::string("_dag.dot"));
724
726 throw ModuleRunTimeError(
727 __FILE__, __LINE__, __func__,
728 (boost::format("Move '%s' failed to schedule")
729 % firstToSchedule->toString()).str());
730 }
731 startCycle = firstToSchedule->cycle();
732 if (firstToSchedule->move().destination().isTriggering()) {
733 trigger = firstToSchedule;
734 } else {
735 lastOperandCycle = std::max(lastOperandCycle, startCycle);
736 }
737 } else {
739 std::string("ii_") +
741 std::string("_dag.dot"));
742
744 throw ModuleRunTimeError(
745 __FILE__, __LINE__, __func__,
746 (boost::format(
747 "Unable to schedule '%s' is there enough resources?")
748 % moves.toString()).str());
749 }
750
751 // try to schedule all input moveNodes, except trigger
752 for (int moveIndex = 0; moveIndex < moves.nodeCount(); ++moveIndex) {
753 MoveNode& moveNode = moves.node(moveIndex);
754 // skip the result reads
755 if (!moveNode.isDestinationOperation()) {
756 continue;
757 }
758
759 if (moveNode.isScheduled()) {
760 earliestScheduledOperand =
761 std::min(earliestScheduledOperand, moveNode.cycle());
762 if (moveNode.move().destination().isTriggering()) {
763 trigger = &moveNode;
764 }
765 continue;
766 }
767
768 // in case the operand move requires register copies due to
769 // missing connectivity, schedule them first
770 scheduleInputOperandTempMoves(moveNode, moveNode);
771 scheduleMove(moveNode, cycle, true);
772
773 if (moveNode.isScheduled()) {
774 earliestScheduledOperand =
775 std::min(earliestScheduledOperand, moveNode.cycle());
776 if (moveNode.move().destination().isTriggering()) {
777 trigger = &moveNode;
778 earliestScheduledOperand =
779 std::min(earliestScheduledOperand, moveNode.cycle());
780 } else {
781 lastOperandCycle =
782 std::max(lastOperandCycle, moveNode.cycle());
783 }
784 } else {
785 // Movenode scheduling failed.
787 // try to unschedule trigger and then schedule this operand.
788 if (trigger != NULL && trigger->isScheduled()) {
789 unschedule(*trigger);
791 scheduleInputOperandTempMoves(moveNode, moveNode);
792 scheduleMove(moveNode, cycle, true);
793 if (!moveNode.isScheduled()) {
794 // if still failed return -1
796 // but make sure high-level loop advances some more cycles
797 if (earliestScheduledOperand != INT_MAX) {
798 cycle = earliestScheduledOperand;
799 }
800 return -1;
801 }
802 earliestScheduledOperand =
803 std::min(earliestScheduledOperand, moveNode.cycle());
804 lastOperandCycle =
805 std::max(lastOperandCycle, moveNode.cycle());
806 } else {
807 // failed even though no trigger scheduled.
808 // but make sure high-level loop advances some more cycles
809 if (earliestScheduledOperand != INT_MAX) {
810 cycle = earliestScheduledOperand;
811 }
812 return -1;
813 }
814 }
815 }
816
817 // if trigger unscheduled, schedule it again.
818 assert(trigger != NULL);
819 if (!trigger->isScheduled()) {
820 scheduleInputOperandTempMoves(*trigger, *trigger);
821 scheduleMove(*trigger, lastOperandCycle, true);
822
823 if (!trigger->isScheduled()) {
824 // trigger scheduling failed.
826 return -1;
827 }
828 earliestScheduledOperand =
829 std::min(earliestScheduledOperand, trigger->cycle());
830 }
831
832 return earliestScheduledOperand;
833}
834/**
835 * Schedules the result read moves of an operation execution.
836 *
837 * Assumes the given MoveNodeGroup contains all moves in the operation
838 * execution. Also assumes that all operand moves have been scheduled.
839 *
840 * @param moves Moves of the operation execution.
841 */
842bool
844 int tempRegLimitCycle = 0;
845
846 for (int moveIndex = 0; moveIndex < moves.nodeCount(); ++moveIndex) {
847 MoveNode& moveNode = moves.node(moveIndex);
848
849 if (!moveNode.isScheduled()) {
850 if (!moveNode.isSourceOperation()) {
851 throw InvalidData(
852 __FILE__, __LINE__, __func__,
853 (boost::format("Move to schedule '%s' is not "
854 "result move!") % moveNode.toString()).str());
855 }
856 if (succeedingTempMove(moveNode) != NULL) {
857
858 MoveNode* lastRead =
860 moveNode.move().destination().registerFile(),
861 moveNode.move().destination().index());
862 if (lastRead != NULL) {
863 tempRegLimitCycle = lastRead->cycle();
864 }
865 }
866
867 scheduleMove(moveNode, tempRegLimitCycle, true);
868 if (!moveNode.isScheduled()) {
869 // Result can not be scheduled even when operands were,
870 // usually caused by operands too early (data dependencies
871 // ok) and result forced to be scheduled much later
872 // because of data dependencies (WA{R,W} dependence with
873 // target register in most cases)
874 return false;
875 }
876 scheduleResultReadTempMoves(moveNode, moveNode, 0);
877 if (!moveNode.isScheduled()) {
878 throw InvalidData(
879 __FILE__, __LINE__, __func__,
880 (boost::format("Move '%s' did not get scheduled!")
881 % moveNode.toString()).str());
882 }
883 }
884 }
885 return true;
886}
887
888/**
889 * Schedules moves in a single operation execution.
890 *
891 * Assumes the given MoveNodeGroup contains all moves in the operation
892 * execution. Also assumes that all inputs to the MoveNodeGroup have
893 * been scheduled.
894 *
895 * @param moveNode R-R Move to schedule.
896 */
897void
899#ifdef DEBUG_REG_COPY_ADDER
900 ddg_->setCycleGrouping(true);
902 (boost::format("%s_before_ddg.dot") % ddg_->name()).str());
903#endif
904
905 RegisterCopyAdder regCopyAdder(
907
908#ifdef DEBUG_REG_COPY_ADDER
909 const int tempsAdded =
910#endif
911
912 regCopyAdder.addRegisterCopiesToRRMove(moveNode, ddg_)
913#ifdef DEBUG_REG_COPY_ADDER
914 .count_
915#endif
916 ;
917#ifdef DEBUG_REG_COPY_ADDER
918 if (tempsAdded > 0) {
920 (boost::format("%s_after_regcopy_ddg.dot") % ddg_->name()).str());
921 }
922 ddg_->sanityCheck();
923#endif
924
925 scheduleMove(moveNode, 0, true);
926 scheduleRRTempMoves(moveNode, moveNode, 0); //Fabio: 0 or more?!?
927}
928
929/**
930 * Schedules a single move to the earliest possible cycle, taking in
931 * account the DDG, resource constraints, and latencies in producing
932 * source values.
933 *
934 * This method assumes the move is possible to schedule with regards to
935 * connectivity and resources. Short immediates are converted to long
936 * immediates when needed.
937 *
938 * @param move The move to schedule.
939 * @param earliestCycle The earliest cycle to try.
940 * @param allowPredicationAndRenaming Whether allowed to remove guard from
941 * the move being scheduled. Checks again side-effects are still made,
942 * this can be done only to operand moves and triggers without mem
943 * write or side-effects.
944 */
945void
947 MoveNode& moveNode, int earliestCycle, bool allowPredicationAndRenaming) {
948 if (moveNode.isScheduled()) {
949 throw InvalidData(
950 __FILE__, __LINE__, __func__,
951 (boost::format("Move '%s' is already scheduled!")
952 % moveNode.toString()).str());
953 }
954
955 int sourceReadyCycle = 0;
956 if (moveNode.isSourceOperation()) {
957 sourceReadyCycle = moveNode.earliestResultReadCycle();
958 }
959
960 int ddgCycle = 0;
961
962 unsigned int ii = rm_->initiationInterval();
963
964 // schedule jumps and calls...
965 if (moveNode.move().isControlFlowMove()) {
966
967 // the branch should get scheduled last, so the DDG should have
968 // only the branch move(s) at hand unscheduled. For software
969 // pipelining (ii > 0), branches might be scheduled earlier.
970 if (ii == 0 && ddg_->nodeCount() != ddg_->scheduledNodeCount() + 1) {
971 // in rare case the branch moves can have 2 parameters (SPU)
972 // and therefore 2 nodes unscheduled. Check if the unscheduled
973 // moves are all control flow moves.
974 DataDependenceGraph::NodeSet unscheduledMoves =
976 for (DataDependenceGraph::NodeSet::iterator i = unscheduledMoves.begin();
977 i != unscheduledMoves.end(); ++i) {
978 if (!(*i)->move().isControlFlowMove()) {
979 TCEString msg =
980 "Control Flow Move is not last of the unscheduled moves! ";
981 msg += "Scheduled count=" +
983 msg += " Node count=" +
985 throw InvalidData(__FILE__, __LINE__, __func__, msg);
986 }
987 }
988 }
989
990 // try to fill the delay slots with moves within the same basic
991 // block
992 // @TODO: ignore argument/rv register deps in case of call as they
993 // are not really used by the call instruction but by the called
994 // procedure, thus we can schedule them at the delay slots
995
996 // largest cycle of DDG is the highest cycle where something
997 // was scheduled, however such a move can have pipeline
998 // that spans more cycles and we should not change
999 // the control flow while something is still in pipeline
1000 int largest = std::max(rm_->largestCycle(), ddg_->largestCycle());
1001 if (ii == 0) {
1002 ddgCycle =
1003 std::max(
1004 ddg_->earliestCycle(moveNode),
1005 largest - targetMachine_->controlUnit()->delaySlots());
1006 } else {
1007 unsigned int delaySlots =
1009 // is the jump in first or later iteration?
1010 MoveNode* jumpLimit = ddg_->findLoopLimitAndIndex(moveNode).first;
1011
1012 ddgCycle = (((ddg_->earliestCycle(moveNode, ii) +
1013 delaySlots) / ii) + 1)*ii - 1 - delaySlots;
1014 if ((unsigned)ddgCycle >= ii - delaySlots) {
1015
1016 int jumpOverlapCount = (ddgCycle + delaySlots) / ii;
1017 if (jumpLimit == NULL) {
1018 // allow jump only in first iteration if we could
1019 // not find the limit
1020 return;
1021 } else {
1022 // update loop limit counter.
1023 TTAProgram::TerminalImmediate& ti = dynamic_cast<
1024 TTAProgram::TerminalImmediate&>(jumpLimit->move().source());
1025 int jumpLimitValue = ti.value().unsignedValue();
1026 int loopCounterStep = 1;
1027 if (jumpLimitValue != tripCount_) {
1028 if (jumpLimitValue == 2 * tripCount_) {
1029 loopCounterStep = 2;
1030 } else {
1031 if (jumpLimitValue == 4 * tripCount_) {
1032 loopCounterStep = 4;
1033 } else {
1034 // don't know how much to decr. counter.
1035 return;
1036 }
1037 }
1038 }
1039
1040 if (tripCount_ > jumpOverlapCount) {
1041 jumpLimit->move().setSource(
1043 SimValue(
1044 jumpLimitValue -
1045 (jumpOverlapCount * loopCounterStep),
1046 ti.value().width())));
1047 } else {
1048 // loop limit is too short.
1049 // would be always executed too many times.
1050 return;
1051 }
1052 }
1053 }
1054 }
1055 } else { // not a control flow move:
1056 ddgCycle = ddg_->earliestCycle(moveNode, ii);
1057
1058 // <pekka> This is now always called even though renaming is disabled?
1059 int minRenamedEC = std::max(
1060 sourceReadyCycle, ddg_->earliestCycle(moveNode, ii, true, true));
1061
1062 // rename if can and may alow scheuduling earlier.
1063 if (renamer_ != NULL && minRenamedEC < ddgCycle &&
1064 allowPredicationAndRenaming) {
1065 minRenamedEC = rm_->earliestCycle(minRenamedEC, moveNode);
1066 if (minRenamedEC < ddgCycle) {
1067
1069 moveNode, ii != 0, true, true, minRenamedEC)) {
1070 ddgCycle = ddg_->earliestCycle(moveNode, ii);
1071 } else {
1072#ifdef THIS_IS_BUGGY_WITH_REGCOPY_ADDER
1073 // renaming already scheduled ones may fail if
1074 // loop scheudling.
1075 if (rm_->initiationInterval() == 0) {
1076 MoveNode *limitingAdep =
1078 if (limitingAdep != NULL) {
1079 // don't try to rename is same operation.
1080 // as it would not help at all.
1081 if (!moveNode.isSourceOperation() ||
1082 !limitingAdep->isDestinationOperation() ||
1083 &moveNode.sourceOperation() !=
1084 &limitingAdep->destinationOperation()) {
1086 *limitingAdep, false, true, true)) {
1087 ddgCycle = ddg_->earliestCycle(
1088 moveNode);
1089 }
1090 }
1091 }
1092 }
1093#endif
1094 }
1095 }
1096 }
1097 }
1098
1099 // if it's a conditional move then we have to be sure that the guard
1100 // is defined before executing the move.
1101 // this is already handled by DDGs earliestCycle, except cases
1102 // where the guard is defined in a previous BB.
1103 // So this prevents scheduling unconditional moves at the beginning
1104 // of a BB.
1105
1106 if (!moveNode.move().isUnconditional()) {
1107 ddgCycle = std::max(ddgCycle, moveNode.guardLatency()-1);
1108
1109 if (allowPredicationAndRenaming) {
1110 // try to get rid of the guard if it gives earlier earliestcycle.
1111 if (ddg_->earliestCycle(moveNode, ii, false, false, true)
1112 < ddgCycle) {
1113 bool guardNeeded = false;
1114 if (moveNode.move().destination().isGPR() ||
1115 moveNode.move().isControlFlowMove()) {
1116 guardNeeded = true;
1117 } else {
1118 // this would be needed only for trigger,
1119 // but trigger may change during scheduling.
1120 // so lets just limit all operands, it seems
1121 // this does not make the schedule any worse.
1122 if (moveNode.isDestinationOperation()) {
1123 const Operation& o =
1124 moveNode.destinationOperation().operation();
1125 if (o.writesMemory() || o.hasSideEffects() ||
1126 o.affectsCount() != 0) {
1127 guardNeeded = true;
1128 }
1129 }
1130 }
1131 if (!guardNeeded) {
1132 moveNode.move().setGuard(NULL);
1133 ddg_->removeIncomingGuardEdges(moveNode);
1134 ddgCycle = ddg_->earliestCycle(moveNode, ii);
1135 assert(moveNode.move().isUnconditional());
1136 }
1137 }
1138
1139 if (ii == 0 && tryToOptimizeWaw(moveNode)) {
1140 ddgCycle = std::max(ddg_->earliestCycle(moveNode, ii),
1141 moveNode.guardLatency()-1);
1142 }
1143
1144 }
1145 }
1146
1147 // Earliest cycle from which to start, could depend on result ready
1148 // for result moves.
1149 int minCycle = std::max(std::max(earliestCycle, ddgCycle), minCycle_);
1150 minCycle = std::max(minCycle, sourceReadyCycle);
1151
1152 // RM hasConnection takes MoveNodeSet, however is called only for one
1153 // moveNode here.
1154 MoveNodeSet tempSet;
1155 tempSet.addMoveNode(moveNode);
1156 if (moveNode.isSourceConstant() &&
1157 !moveNode.move().hasAnnotations(
1159 // If source is constant and node does not have annotation already,
1160 // we add it if constant can not be transported so IU broker and
1161 // OutputPSocket brokers will add Immediate
1162 // Example : 999999 -> integer0.2
1163 if (!rm_->canTransportImmediate(moveNode)){
1166 moveNode.move().setAnnotation(annotation);
1167
1168 } else if (!moveNode.isDestinationOperation() &&
1169 rm_->earliestCycle(rm_->largestCycle() + 1, moveNode) == -1
1170 && rm_->initiationInterval() == 0) {
1171
1172 // If source is constant and node does not have annotation already,
1173 // we add it if node has no connection, so IU broker and
1174 // OutputPSocket brokers will add Immediate
1175 // Example: 27 -> integer0.2
1176 // With bus capable of transporting 27 as short immediate but
1177 // no connection from that bus to integer0 unit
1178 // this may mess up modulo scheduling.
1179 // TODO: do this more cleanly, this is a kludge.
1182 moveNode.move().setAnnotation(annotation);
1183 }
1184 }
1185 // annotate the return move otherwise it might get undetected in the
1186 // simulator after the short to long immediate conversion and thus
1187 // stopping simulation automatically might not work
1188 if (moveNode.isSourceConstant() &&
1189 moveNode.move().isReturn() &&
1190 !rm_->canTransportImmediate(moveNode)) {
1193 moveNode.move().setAnnotation(annotation);
1194 }
1195
1196 minCycle = rm_->earliestCycle(minCycle, moveNode);
1197 if (minCycle == -1 || minCycle == INT_MAX) {
1198 if (moveNode.isSourceConstant() &&
1199 !moveNode.isDestinationOperation() &&
1200 moveNode.move().hasAnnotations(
1202 // If earliest cycle returns -1 and source is constant and moveNode
1203 // needs long immediate there is most likely missing long immediate
1204 // unit
1205 std::string msg = "Assignment of MoveNode " + moveNode.toString();
1206 msg += " failed! Most likely missing Long Immediate Unit";
1207 msg += " or Instruction Template!";
1208 if (rm_->initiationInterval() == 0) {
1209 ddg_->writeToDotFile("illegalmach.dot");
1210 throw IllegalMachine(
1211 __FILE__, __LINE__, __func__, msg);
1212 } else {
1214 std::string("ii_") +
1216 std::string("_dag.dot"));
1217
1219 throw ModuleRunTimeError(
1220 __FILE__, __LINE__, __func__, msg);
1221 }
1222 }
1223 return;
1224 }
1225
1226 int latestDDG = ddg_->latestCycle(moveNode, ii);
1227
1228 if (ii != 0 && (minCycle > latestDDG)) {
1229
1230 if (ddg_->latestCycle(moveNode, ii, true) > latestDDG) {
1231
1232 if (renamer_ != NULL && allowPredicationAndRenaming) {
1233 // todo: do also otherway
1234 if (renamer_->renameSourceRegister(moveNode,true, true, true)) {
1235 latestDDG = ddg_->latestCycle(moveNode, ii);
1236 }
1237 }
1238 }
1239 }
1240
1241 // test again after renaming?
1242
1243 if (ii != 0 && (minCycle > latestDDG)) {
1244
1245 // if we pre-scheduled jump, unschedule it and try to schdule
1246 // the node again.
1247 if (jumpNode_ != NULL) {
1248 ddg_->writeToDotFile("unschedJump.dot");
1250 jumpNode_ = NULL;
1251 scheduleMove(moveNode, earliestCycle, allowPredicationAndRenaming);
1252 return;
1253 }
1254
1256 std::string("ii_") + Conversion::toString(ii) +
1257 std::string("_dag.dot"));
1258
1260 throw ModuleRunTimeError(
1261 __FILE__, __LINE__, __func__, "Schedule failed try bigger ii.");
1262 }
1263
1264 try {
1265 rm_->assign(minCycle, moveNode);
1266 } catch (const Exception& e) {
1267 throw ModuleRunTimeError(
1268 __FILE__, __LINE__, __func__, e.errorMessageStack());
1269 }
1270
1271 if (!moveNode.isScheduled()) {
1272 throw ModuleRunTimeError(
1273 __FILE__, __LINE__, __func__,
1274 (boost::format("Assignment of MoveNode '%s' failed!")
1275 % moveNode.toString()).str());
1276 }
1277 assert(moveNode.cycle() >= minCycle_);
1278}
1279
1280/**
1281 * Schedules the (possible) temporary register copy moves (due to missing
1282 * connectivity) succeeding the given RR move.
1283 *
1284 * The function recursively goes through all the temporary moves added to
1285 * the given RR move.
1286 *
1287 * @param regToRegMove A temp move whose successor has to be scheduled.
1288 * @param firstMove The original move of which temp moves to schedule.
1289 * @param lastUse Recursive function parameter, it should be set as 0
1290 * for the first function call.
1291 */
1292void
1294 MoveNode& regToRegMove, MoveNode& firstMove, int lastUse) {
1295 /* Because temporary register moves do not have WaR/WaW dependency edges
1296 between the other possible uses of the same temp register in
1297 the same operation, we have to limit the scheduling of the new
1298 temp reg move to the last use of the same temp register so
1299 we don't overwrite the temp registers of the other operands. */
1300
1301 // find all unscheduled succeeding moves of the result read move
1302 // there should be only only one with the only dependence edge (RaW) to
1303 // the result move
1304
1305 MoveNode* tempMove1 = succeedingTempMove(regToRegMove);
1306 if (tempMove1 == NULL)
1307 return; // no temp moves found
1308
1309 MoveNode* tempMove2 = succeedingTempMove(*tempMove1);
1310 if (tempMove2 != NULL) {
1311 MoveNode* lastRead =
1313 tempMove1->move().destination().registerFile(),
1314 tempMove1->move().destination().index());
1315 if (lastRead != NULL)
1316 lastUse = lastRead->cycle();
1317 }
1318
1319 scheduleMove(*tempMove1, lastUse, true);
1320 assert(tempMove1->isScheduled());
1321 scheduledTempMoves_[&firstMove].insert(tempMove1);
1322
1323 // the temp move should have maximum of one unscheduled
1324 // succeeding additional reg to reg copy (in case of two temp reg copies),
1325 // schedule it also if found
1326 scheduleResultReadTempMoves(*tempMove1, firstMove, lastUse+1);
1327}
1328
1329/**
1330 * Schedules the (possible) temporary register copy moves (due to missing
1331 * connectivity) preceeding the given input move.
1332 *
1333 * The function recursively goes through all the temporary moves added to
1334 * the given input move.
1335 *
1336 * @param operandMove A temp move whose predecessor has to be scheduled.
1337 * @param operandWrite The original move of which temp moves to schedule.
1338 */
1339void
1341 MoveNode& operandMove, MoveNode& operandWrite) {
1342 /* Because temporary register moves do not have WaR dependency edges
1343 between the other possible uses of the same temp register in
1344 the same operation, we have to limit the scheduling of the new
1345 temp reg move to the last use of the same temp register so
1346 we don't overwrite the temp registers of other operands. */
1347
1348 /* <pekka> TODO: this could be improved by allowing to schedule also
1349 *before* the previous use, in the "holes" where it's unused. Now in effect
1350 the copies serialize the code quite badly. */
1351 int lastUse = 0;
1352
1353 // find all unscheduled preceeding temp moves of the operand move
1354 DataDependenceGraph::EdgeSet inEdges = ddg_->inEdges(operandMove);
1355 MoveNode* tempMove = NULL;
1356 for (DataDependenceGraph::EdgeSet::iterator i = inEdges.begin();
1357 i != inEdges.end(); ++i) {
1358 if ((**i).edgeReason() != DataDependenceEdge::EDGE_REGISTER ||
1359 (**i).guardUse() ||
1360 (**i).dependenceType() != DataDependenceEdge::DEP_RAW) {
1361 continue;
1362 }
1363
1364 MoveNode& m = ddg_->tailNode(**i);
1365 if (m.isScheduled() ||
1366 !m.move().hasAnnotations(
1368 continue;
1369 }
1370
1371 assert(tempMove == NULL &&
1372 "Multiple unscheduled moves for the operand move, should have "
1373 "max. one (the temporary move)!");
1374 tempMove = &m;
1375 MoveNode* lastRead =
1377 tempMove->move().destination().registerFile(),
1378 tempMove->move().destination().index());
1379 if (lastRead != NULL)
1380 lastUse = lastRead->cycle();
1381 }
1382
1383 if (tempMove == NULL)
1384 return; // no temp moves found
1385
1386 // the temp move should have maximum of one unscheduled
1387 // preceeding reg to reg copy (in case of an additional temp reg copy),
1388 // schedule it first if found, calling recursively this function
1389 scheduleInputOperandTempMoves(*tempMove, operandWrite);
1390
1391 // TODO: is the true correct here?
1392 scheduleMove(*tempMove, lastUse, true);
1393 assert(tempMove->isScheduled());
1394 scheduledTempMoves_[&operandWrite].insert(tempMove);
1395}
1396
1397/**
1398 * Finds the temp move succeeding the given movenode.
1399 *
1400 * If it does not have one , returns null.
1401 *
1402 * @param current MoveNode whose tempmoves we are searching
1403 * @return tempMove succeeding given node, or null if does not exist.
1404 */
1405MoveNode*
1407
1409 MoveNode* result = NULL;
1410 for (DataDependenceGraph::NodeSet::iterator i = succ.begin();
1411 i != succ.end(); ++i) {
1412 MoveNode& m = **i;
1413 if (m.isScheduled() ||
1414 !m.move().hasAnnotations(
1416 continue;
1417
1418 assert(result == NULL &&
1419 "Multiple candidates for the temp move of result read.");
1420 result = &m;
1421 }
1422 return result;
1423}
1424
1425/**
1426 * Schedules the (possible) temporary register copy moves (due to missing
1427 * connectivity) succeeding the given result read move.
1428 *
1429 * The function recursively goes through all the temporary moves added to
1430 * the given input move.
1431 *
1432 * @param resultMove A temp move whose successor has to be scheduled.
1433 * @param resultRead The original move of which temp moves to schedule.
1434 * @param lastUse Recursive function parameter, it should be set as 0
1435 * for the first function call.
1436 */
1437void
1439 MoveNode& resultMove, MoveNode& resultRead, int lastUse) {
1440 /* Because temporary register moves do not have WaR/WaW dependency edges
1441 between the other possible uses of the same temp register in
1442 the same operation, we have to limit the scheduling of the new
1443 temp reg move to the last use of the same temp register so
1444 we don't overwrite the temp registers of the other operands. */
1445
1446
1447 // find all unscheduled succeeding moves of the result read move
1448 // there should be only only one with the only dependence edge (RaW) to
1449 // the result move
1450
1451 MoveNode* tempMove1 = succeedingTempMove(resultMove);
1452 if (tempMove1 == NULL)
1453 return; // no temp moves found
1454
1455 MoveNode* tempMove2 = succeedingTempMove(*tempMove1);
1456 if (tempMove2 != NULL) {
1457 MoveNode* lastRead =
1459 tempMove1->move().destination().registerFile(),
1460 tempMove1->move().destination().index());
1461 if (lastRead != NULL)
1462 lastUse = lastRead->cycle();
1463 }
1464
1465 scheduleMove(*tempMove1, lastUse, true);
1466 assert(tempMove1->isScheduled());
1467 scheduledTempMoves_[&resultRead].insert(tempMove1);
1468
1469 // the temp move should have maximum of one unscheduled
1470 // succeeding additional reg to reg copy (in case of two temp reg copies),
1471 // schedule it also if found
1472 scheduleResultReadTempMoves(*tempMove1, resultRead, lastUse+1);
1473}
1474
1475/**
1476 * Unschedules the given move.
1477 *
1478 * Also restores a possible short immediate source in case it was converted
1479 * to a long immediate register read during scheduling.
1480 *
1481 * @param moveNode Move to unschedule.
1482 */
1483void
1485 if (moveNode.isScheduled()) {
1486 rm_->unassign(moveNode);
1487 }
1488
1489 if (moveNode.move().hasAnnotations(
1491 // If we added annotation during scheduleMove delete it
1492 moveNode.move().removeAnnotations(
1494 }
1495 if (moveNode.isScheduled() || moveNode.isPlaced()) {
1496 throw InvalidData(
1497 __FILE__, __LINE__, __func__,
1498 (boost::format("Unscheduling of move '%s' failed!")
1499 % moveNode.toString()).str());
1500 }
1501}
1502
1503/**
1504 * Calls unschedule() for all ddg nodes, which are scheduled.
1505 */
1506void
1509 for (DataDependenceGraph::NodeSet::iterator i = scheduled.begin();
1510 i != scheduled.end(); ++i) {
1511 if (softwareBypasser_ != NULL) {
1512 softwareBypasser_->removeBypass(**i, *ddg_, *rm_, false);
1513 }
1514 unschedule(**i);
1515 }
1516 if (softwareBypasser_ != NULL) {
1518 }
1519}
1520
1521/**
1522 * Unschedules the (possible) temporary register copy moves (due to missing
1523 * connectivity) preceeding the given move.
1524 *
1525 * @param operandMove The move of which temp moves to unschedule.
1526 */
1527void
1529
1530 if (!MapTools::containsKey(scheduledTempMoves_, &operandMove))
1531 return; // nothing to unschedule
1532
1533 DataDependenceGraph::NodeSet tempMoves = scheduledTempMoves_[&operandMove];
1534
1535 for (DataDependenceGraph::NodeSet::iterator i = tempMoves.begin();
1536 i != tempMoves.end(); ++i) {
1537 unschedule(**i);
1538 }
1539 scheduledTempMoves_.erase(&operandMove);
1540}
1541
1542/**
1543 * Unschedules the (possible) temporary register copy moves (due to missing
1544 * connectivity) succeeding the given result read move.
1545 *
1546 * @param resultMove The move of which temp moves to unschedule.
1547 */
1548void
1550
1551 if (!MapTools::containsKey(scheduledTempMoves_, &resultMove))
1552 return; // nothing to unschedule
1553
1554 DataDependenceGraph::NodeSet tempMoves = scheduledTempMoves_[&resultMove];
1555
1556 for (DataDependenceGraph::NodeSet::iterator i = tempMoves.begin();
1557 i != tempMoves.end(); ++i) {
1558 unschedule(**i);
1559 }
1560 scheduledTempMoves_.erase(&resultMove);
1561}
1562
1563/**
1564 * A short description of the pass, usually the optimization name,
1565 * such as "basic block scheduler".
1566 *
1567 * @return The description as a string.
1568 */
1569std::string
1571 return "Instruction scheduler with a basic block scope.";
1572}
1573
1574/**
1575 * Optional longer description of the pass.
1576 *
1577 * This description can include usage instructions, details of choice of
1578 * algorithmic details, etc.
1579 *
1580 * @return The description as a string.
1581 */
1582std::string
1584 return
1585 "Basic block scheduler that uses the longest path information of "
1586 "data dependency graph to prioritize the ready list. Assumes that "
1587 "the input has registers allocated and no connectivity missing.";
1588}
1589
1590/**
1591 * Notifies to the selector that given nodes and their temp reg copies are
1592 * scheduled .
1593 *
1594 * @param nodes nodes which are scheduled.
1595 * @param selector selector which to notify.
1596 */
1597void
1599 MoveNodeGroup& moves, MoveNodeSelector& selector) {
1600
1601 for (int moveIndex = 0; moveIndex < moves.nodeCount(); ++moveIndex) {
1602 MoveNode& moveNode = moves.node(moveIndex);
1603 selector.notifyScheduled(moveNode);
1604 std::map<const MoveNode*, DataDependenceGraph::NodeSet>::
1605 iterator tmIter = scheduledTempMoves_.find(&moveNode);
1606 if (tmIter != scheduledTempMoves_.end()) {
1607 DataDependenceGraph::NodeSet& tempMoves = tmIter->second;
1608 for (DataDependenceGraph::NodeSet::iterator i =
1609 tempMoves.begin(); i != tempMoves.end(); i++) {
1610 if ((**i).isScheduled()) {
1611 selector.notifyScheduled(**i);
1612 }
1613 }
1614 }
1615 }
1616}
1617
1618/**
1619 * Prints DDG to a dot file before and after scheudling
1620 *
1621 * @param ddg to print
1622 * @param name operation name for ddg
1623 * @param final specify if ddg is after scheduling
1624 * @param resetCounter
1625 * @param format format of the file
1626 * @return number of cycles/instructions copied.
1627 */
1628void
1631 const std::string& name,
1633 bool final,
1634 bool resetCounter) const {
1635
1636 static int bbCounter = 0;
1637
1638 if (resetCounter) {
1639 bbCounter = 0;
1640 }
1641
1642 if (final) {
1643 if (format == DataDependenceGraph::DUMP_DOT) {
1644 ddg.writeToDotFile(
1645 (boost::format("bb_%s_%s_after_scheduling.dot") %
1646 ddg_->name() % name).str());
1647 } else {
1648 ddg.writeToXMLFile(
1649 (boost::format("bb_%s_%s_after_scheduling.xml") %
1650 ddg_->name() % name).str());
1651 }
1652
1653 } else {
1654 if (format == DataDependenceGraph::DUMP_DOT) {
1655 ddg.writeToDotFile(
1656 (boost::format("bb_%s_%d_%s_before_scheduling.dot")
1657 % ddg.name() % bbCounter % name).str());
1658 DataDependenceGraph* criticalPath = ddg.criticalPathGraph();
1659 criticalPath->writeToDotFile(
1660 (boost::format(
1661 "bb_%s_%d_%s_before_scheduling_critical_path.dot")
1662 % ddg.name() % bbCounter % name).str());
1663 delete criticalPath;
1664 } else {
1665 ddg.writeToXMLFile(
1666 (boost::format("bb_%s_%d_%s_before_scheduling.xml")
1667 % ddg.name() % bbCounter % name).str());
1668 }
1669 }
1670 ++bbCounter;
1671}
1672
1673/**
1674 *
1675 * Returns the operand which is triggering in all FU's which support
1676 * the operation. If operation not found, is not gcu or
1677 * multiple FU's have different triggers, returns 0
1678 */
1679int
1681 const Operation& operation,
1683
1684 const TCEString& opName = operation.name();
1687
1688 int trigger = 0;
1689 for (int i = 0; i < fuNav.count(); i++) {
1690 TTAMachine::FunctionUnit& fu = *fuNav.item(i);
1691 if (fu.hasOperation(opName)) {
1692 TTAMachine::HWOperation& hwop = *fu.operation(opName);
1693 for (int j = 1; j <= operation.numberOfInputs(); j++) {
1694 TTAMachine::FUPort& port = *hwop.port(j);
1695 if (port.isTriggering()) {
1696 if (trigger == 0) {
1697 trigger = j;
1698 } else {
1699 if (trigger != j) {
1700 return 0;
1701 }
1702 }
1703 }
1704 }
1705 }
1706 }
1707 return trigger;
1708}
1709
1710/**
1711 * If the operation is commutative, tries to change the operands so that
1712 * the scheduler can schedule it better.
1713 *
1714 * If the operation is not commutative, does nothing.
1715 *
1716 * Which is better depends lots on the code being compiled and architecture
1717 * which we are compiling for.
1718 *
1719 * Current implementation tries to make constants triggers,
1720 * and if there are no constant inputs, try to also change inputs so
1721 * that last ready operand becomes trigger if it's near,
1722 * but first ready operand becomes trigger if it's further
1723 * (allows better bypassing of other operands)
1724 * This seems to work in practice
1725 *
1726 * @param po programoperation whose inputs to switch
1727 * @return true if changed inputs, false if not.
1728 */
1729bool
1731 const Operation& op = po.operation();
1732 if (op.numberOfInputs() == 2 && op.canSwap(1, 2)) {
1733
1734 int triggerOperand = getTriggerOperand(op, *targetMachine_);
1735
1736 if (triggerOperand != 0) {
1737 MoveNode* latest = NULL;
1738 int latestMinCycle = -1;
1739 int firstMinCycle = INT_MAX;
1740
1741 //check all input moves.
1742 for (int i = 0; i < po.inputMoveCount(); i++) {
1743 MoveNode& node = po.inputMove(i);
1744 // always make constants triggers.
1745 // todo: shoudl do this only with short imms?
1746 if (node.isSourceConstant()) {
1747 int operandIndex =
1748 node.move().destination().operationIndex();
1749 if (operandIndex != triggerOperand) {
1750 po.switchInputs();
1751 return true;
1752 } else {
1753 return false;
1754 }
1755 }
1756
1757 int minCycle = ddg_->earliestCycle(
1758 node, rm_->initiationInterval(), false, false, true, true);
1759 if (minCycle > latestMinCycle) {
1760 latest = &node;
1761 latestMinCycle = minCycle;
1762 }
1763 if (minCycle < firstMinCycle) {
1764 firstMinCycle = minCycle;
1765 }
1766 }
1767
1768 int lastOperand = latest->move().destination().operationIndex();
1769
1770 // don't change if min cycles of first and alst are equal.
1771 if (latestMinCycle == firstMinCycle) {
1772 return false;
1773 }
1774 if (latestMinCycle - firstMinCycle > 1) { // reverse logic if far.
1775 if (lastOperand == triggerOperand) {
1776 po.switchInputs();
1777 return true;
1778 }
1779 } else {
1780 if (lastOperand != triggerOperand) {
1781 po.switchInputs();
1782 return true;
1783 }
1784 }
1785 }
1786 }
1787 return false;
1788}
1789
1790/**
1791 * Checks if the result moves that were removed might remove some
1792 * resource bottleneck of already scheduled moves.
1793 *
1794 * This situation happens at least when a result move used the last RF write
1795 * port for a cycle and limited the earliest cycle of a previously scheduled
1796 * move. Thus, after the last use of that result was scheduled and bypassed,
1797 * the RF write port is freed as the result move becomes unnecessary, thus
1798 * the move that was scheduled before could be scheduled earlier. Same
1799 * thing can happen for bus (move slot) contrained moves and also moves
1800 * that were restricted by a WaR edge due to the result move, etc.
1801 */
1802void
1804 std::set<std::pair<TTAProgram::Move*, int> > removedMoves) {
1805
1806 if (removedMoves.size() == 0)
1807 return;
1808
1809#ifndef RESCHEDULE_NEXT_CYCLE_AFTER_DRE
1810 return;
1811#endif
1812
1813 const bool DEBUG_PRINT = false; //Application::verboseLevel() > 0;
1814
1815 if (DEBUG_PRINT) {
1817 << removedMoves.size() << " dead results eliminated: "
1818 << std::endl;
1819 }
1820
1821 for (std::set<std::pair<TTAProgram::Move*, int> >::
1822 const_iterator i = removedMoves.begin();
1823 i != removedMoves.end(); ++i) {
1824
1825 TTAProgram::Move& eliminatedMove = *(*i).first;
1826 int cycle = (*i).second;
1827
1828 if (DEBUG_PRINT) {
1830 << eliminatedMove.toString() << " (cycle " << cycle << ") ";
1831 }
1832
1833 int oldCycle = cycle + 1;
1834 // look for already scheduled moves that were scheduled after
1835 // the cycle of the result move, thus potentially were limited by
1836 // the result move earlier
1837 DataDependenceGraph::NodeSet nextCycleMoves =
1838 ddg_->movesAtCycle(oldCycle);
1839
1840 // if true, try to reschedule all moves at the next cycle (slower,
1841 // but handles the case when the cycle was bus constrained previously),
1842 // if false, tries to schedule only potentially RF write port
1843 // constrained moves (faster)
1844 bool rescheduleAllSucceedingMoves = false;
1845 for (DataDependenceGraph::NodeSet::const_iterator m =
1846 nextCycleMoves.begin(); m != nextCycleMoves.end(); ++m) {
1847 MoveNode& moveNode = **m;
1848
1849 if (rescheduleAllSucceedingMoves ||
1850 (moveNode.move().destination().isGPR() &&
1851 &eliminatedMove.destination().registerFile() ==
1852 &moveNode.move().destination().registerFile())) {
1853
1854 if (DEBUG_PRINT) {
1856 << "Trying to reschedule "
1857 << moveNode.toString() << " " << std::endl;
1858 }
1859
1860 assert(moveNode.isScheduled() && moveNode.cycle() == oldCycle);
1861
1862 unschedule(moveNode);
1863 // because a pontially removed WaR, we might be able to
1864 // schedule this move (write to the same reg) even earlier
1865 // than the dead result move cycle
1866 scheduleMove(moveNode, std::max(oldCycle - 10, 0), false);
1867
1868 if (!moveNode.isScheduled()) {
1869 for (int c = 4; c >= 0; --c) {
1871 << "\t\t" << oldCycle - c << ": "
1872 << rm_->instruction(
1873 std::max(oldCycle - c, 0))->toString()
1874 << std::endl;
1875 }
1876 assert(moveNode.isScheduled());
1877 }
1878
1879 if (moveNode.cycle() < oldCycle) {
1880 if (DEBUG_PRINT) {
1882 << " OK at cycle " << moveNode.cycle() << ". " << std::endl;
1883 }
1884 } else {
1885 // should not get worse schedule!
1886 assert(moveNode.cycle() == oldCycle);
1887 if (DEBUG_PRINT) {
1888 Application::logStream() << " NO change. " << std::endl;
1889 }
1890 }
1891
1892 }
1893 /// @todo: reschedule also the moves dependent from the
1894 /// rescheduled ones that got earlier
1895 }
1896
1897 if (DEBUG_PRINT) {
1898 Application::logStream() << std::endl;
1899 }
1900 }
1901}
1902
1903void
1905 if (Application::verboseLevel() > 0) {
1906 }
1907}
1908
1909/**
1910 * Tries to get rid of WaW dependence from unconditional to conditional.
1911 *
1912 * if the conditional move's result is overwritten by the cond for
1913 * all users, make the unconditional move conditional, with opposite guard.
1914 */
1915bool
1917
1918 if (ddg_->earliestCycle(moveNode, 0 , false, true) >=
1919 ddg_->earliestCycle(moveNode)) {
1920 return false;
1921 }
1922
1923 MoveNode* wawPred = NULL;
1924 DataDependenceEdge* wawEdge = NULL;
1925
1926 DataDependenceGraph::EdgeSet inEdges = ddg_->inEdges(moveNode);
1927 for (DataDependenceGraph::EdgeSet::iterator i = inEdges.begin();
1928 i != inEdges.end(); i++) {
1929 DataDependenceEdge* e = *i;
1930
1933 !e->tailPseudo()) {
1934 if (wawPred == NULL) {
1935 wawPred = &ddg_->tailNode(**i);
1936 wawEdge = e;
1937 } else {
1938 return false;
1939 }
1940 }
1941 }
1942 if (wawPred == NULL) {
1943 return false;
1944 }
1945
1946 assert(wawPred->isScheduled());
1947 int wawPredCycle = wawPred->cycle();
1948
1949 if (!wawPred->move().isUnconditional()) {
1950 return false;
1951 }
1952
1953 DataDependenceGraph::NodeSet gdMoves = ddg_->guardDefMoves(moveNode);
1954 for (DataDependenceGraph::NodeSet::iterator i = gdMoves.begin();
1955 i != gdMoves.end(); i++) {
1956 MoveNode& mn = **i;
1957 assert(mn.isScheduled());
1958 if (mn.cycle() + moveNode.guardLatency() > wawPredCycle) {
1959 return false;
1960 }
1961 }
1962
1963 // check that no other usage of the data.
1964 DataDependenceGraph::NodeSet consumers = ddg_->regRawSuccessors(*wawPred);
1965 DataDependenceGraph::NodeSet consumers2 = ddg_->regRawSuccessors(moveNode);
1966 for (DataDependenceGraph::NodeSet::iterator i = consumers.begin();
1967 i != consumers.end(); i++) {
1968 MoveNode* mn = *i;
1969 if (consumers2.find(mn) == consumers2.end() &&
1970 (mn->move().isUnconditional() ||
1971 !ddg_->exclusingGuards(*mn, moveNode))) {
1972 return false;
1973 }
1974 }
1975
1976 unschedule(*wawPred);
1977
1979 wawPred->move().setGuard(cg.createInverseGuard(moveNode.move().guard()));
1980
1981 ddg_->copyDepsOver(*wawPred, true, false);
1982
1983 ddg_->copyIncomingGuardEdges(moveNode, *wawPred);
1984 ddg_->copyOutgoingGuardWarEdges(moveNode, *wawPred);
1985
1986 ddg_->removeEdge(*wawEdge);
1987
1988 bool revert = false;
1989
1990 try {
1991 scheduleMove(*wawPred, wawPredCycle, false);
1992
1993 if (!wawPred->isScheduled()) {
1994 revert = true;
1995 }
1996 if (wawPred->isScheduled() && wawPred->cycle() > wawPredCycle) {
1997 unschedule(*wawPred);
1998 revert = true;
1999 }
2000 } catch (Exception&e) {
2001 revert = true;
2002 }
2003
2004 if (revert) {
2005 wawPred->move().setGuard(NULL);
2006 ddg_->removeIncomingGuardEdges(*wawPred);
2008 scheduleMove(*wawPred, wawPredCycle, false);
2009 ddg_->connectNodes(*wawPred, moveNode, *wawEdge);
2010 return false;
2011 }
2012
2013 return true;
2014}
2015
2016void
2018
2019 // try to then push non-triggers back
2020 // to as close as possible to tirggers.
2021 // this frees up the FU to be used fr other operations before
2022 // this operation.
2023 // first search first cycle when this FU is used.
2024
2025 int ec = INT_MAX;
2026 MoveNode* trigger = NULL;
2027 for (int i = 0; i < moves.nodeCount(); i++) {
2028 MoveNode& moveNode = moves.node(i);
2029 if (!moveNode.isDestinationOperation()) {
2030 continue;
2031 }
2032 if (moveNode.move().isTriggering()) {
2033 trigger = &moveNode;
2034 } else {
2035 int oldCycle = moveNode.cycle();
2036 if (oldCycle < ec) {
2037 ec = oldCycle;
2038 }
2039 }
2040 }
2041
2042 int triggerCycle = trigger->cycle();
2043
2044 bool failed = false;
2045 while (ec < triggerCycle && !failed) {
2046 for (int i = 0; i < moves.nodeCount(); i++) {
2047 MoveNode& moveNode = moves.node(i);
2048 if (!moveNode.isDestinationOperation()) {
2049 continue;
2050 }
2051 if (&moveNode != trigger) {
2052 int oldCycle = moveNode.cycle();
2053 if (oldCycle == ec) {
2054 int latest = ddg_->latestCycle(moveNode);
2055 if (latest > oldCycle) {
2056 latest = std::min(latest, triggerCycle);
2057 assert(latest >= ec);
2058 rm_->unassign(moveNode);
2059 int latestrm = rm_->latestCycle(latest, moveNode);
2060 if (latestrm == ec) {
2061 failed = true;
2062 }
2063 assert(latestrm >= ec);
2064 rm_->assign(latestrm, moveNode);
2065 } else {
2066 failed = true;
2067 }
2068 }
2069 }
2070 }
2071 ec = INT_MAX;
2072 // first earliest scheduled operands.
2073 for (int i = 0; i < moves.nodeCount(); i++) {
2074 MoveNode& moveNode = moves.node(i);
2075 if (!moveNode.isDestinationOperation()) {
2076 continue;
2077 }
2078 if (&moveNode != trigger) {
2079 int oldCycle = moveNode.cycle();
2080 if (oldCycle < ec) {
2081 ec = oldCycle;
2082 }
2083 }
2084 }
2085 }
2086}
2087
2088// find which movenode is the trigger of an operation.
2089MoveNode*
2091 const ProgramOperation& po, const TTAMachine::Machine& mach) {
2092
2093 MoveNode* triggerFromPO = po.triggeringMove();
2094 if (triggerFromPO) {
2095 return triggerFromPO;
2096 }
2097
2098 // no scheduled moves. getting harder.
2099
2101 mach.functionUnitNavigator();
2102 MoveNode* candidate = NULL;
2103 for (int i = 0; i < nav.count(); i++) {
2104 TTAMachine::FunctionUnit* fu = nav.item(i);
2105 if (fu->hasOperation(po.operation().name())) {
2106 if (candidate == NULL) {
2107 candidate = findTriggerFromUnit(po, *fu);
2108 } else {
2109 // make sure two different FU's don't have different
2110 // trigger ports.
2111 if (findTriggerFromUnit(po, *fu) != candidate) {
2112 return NULL;
2113 }
2114 }
2115 }
2116 }
2117
2118 if (mach.controlUnit()->hasOperation(po.operation().name())) {
2119 return findTriggerFromUnit(po, *mach.controlUnit());
2120 }
2121 return candidate;
2122
2123}
2124
2125MoveNode*
2127 const ProgramOperation& po, const TTAMachine::Unit& unit) {
2128 const TTAMachine::FunctionUnit* fu =
2129 dynamic_cast<const TTAMachine::FunctionUnit*>(&unit);
2130 int ioIndex = -1;
2131
2132 int portC = fu->portCount();
2133 for (int i = 0; i < portC; i++) {
2134 auto p = fu->port(i);
2135 const TTAMachine::FUPort* port = dynamic_cast<const TTAMachine::FUPort*>(p);
2136 if (port != nullptr && port->isTriggering()) {
2137 const TTAMachine::HWOperation* hwop =
2138 fu->operation(po.operation().name());
2139 ioIndex = hwop->io(*port);
2140 return &po.inputNode(ioIndex).at(0);
2141 }
2142 }
2143 return NULL;
2144}
#define debugLog(text)
#define __func__
#define abortWithError(message)
#define assert(condition)
TTAMachine::Machine * machine
the architecture definition of the estimated processor
static CmdLineOptions * cmdLineOptions()
static int verboseLevel()
static std::ostream & logStream()
void scheduleMove(MoveNode &move, int earliestCycle, bool allowPredicationAndRenaming)
void scheduleRRTempMoves(MoveNode &regToRegMove, MoveNode &firstMove, int lastUse)
MoveNodeSelector * selector_
bool tryToSwitchInputs(ProgramOperation &op)
void unscheduleInputOperandTempMoves(MoveNode &operandMove)
static MoveNode * findTriggerFromUnit(const ProgramOperation &po, const TTAMachine::Unit &unit)
bool tryToOptimizeWaw(const MoveNode &moveNode)
void tryToDelayOperands(MoveNodeGroup &moves)
SimpleResourceManager * rm_
Resource Manager of the currently scheduled BB.
static MoveNode * findTrigger(const ProgramOperation &po, const TTAMachine::Machine &mach)
int scheduleOperandWrites(int &cycle, MoveNodeGroup &moves)
bool scheduleResultReads(MoveNodeGroup &moves)
void unscheduleResultReadTempMoves(MoveNode &resultMove)
virtual int handleDDG(DataDependenceGraph &ddg, SimpleResourceManager &rm, const TTAMachine::Machine &targetMachine, int minCycle=0, bool testOnly=false) override
void scheduleRRMove(MoveNode &moveNode)
SoftwareBypasser * softwareBypasser_
The software bypasser to use to bypass registers when possible.
virtual std::string longDescription() const
void scheduleInputOperandTempMoves(MoveNode &operandMove, MoveNode &operandWrite)
const TTAMachine::Machine * targetMachine_
The target machine we are scheduling the program against.
DataDependenceGraph * ddg_
DDG of the currently scheduled BB.
virtual int handleLoopDDG(DataDependenceGraph &ddg, SimpleResourceManager &rm, const TTAMachine::Machine &targetMachine, int tripCount, SimpleResourceManager *prologRM, bool testOnly=false) override
virtual std::string shortDescription() const
std::map< const MoveNode *, DataDependenceGraph::NodeSet > scheduledTempMoves_
Stores the MoveNodes that were scheduled as temp moves during scheduling of the operand move.
void scheduleOperation(MoveNodeGroup &moves)
void handleRemovedResultMoves(std::set< std::pair< TTAProgram::Move *, int > > removedMoves)
int getTriggerOperand(const Operation &operation, const TTAMachine::Machine &machine)
LLVMTCECmdLineOptions * options_
void scheduleResultReadTempMoves(MoveNode &resultMove, MoveNode &resultRead, int lastUse)
int minCycle_
The earliest cycle to schedule moves in. Used to leave room for sched_yield() by the sched_yield() em...
void notifyScheduled(MoveNodeGroup &moves, MoveNodeSelector &selector)
void unschedule(MoveNode &moveNode)
MoveNode * succeedingTempMove(MoveNode &current)
virtual void printStats() const
RegisterRenamer * renamer_
void ddgSnapshot(DataDependenceGraph &ddg, const std::string &name, DataDependenceGraph::DumpFileFormat format, bool final, bool resetCounter=false) const
BasicBlockScheduler(InterPassData &data, SoftwareBypasser *bypasser=NULL, RegisterRenamer *renamer=NULL)
virtual void removeEdge(Edge &e)
int nodeCount() const
virtual NodeSet successors(const Node &node, bool ignoreBackEdges=false, bool ignoreForwardEdges=false) const
Node & node(const int index) const
virtual const TCEString & name() const
virtual Node & tailNode(const Edge &edge) const
virtual EdgeSet inEdges(const Node &node) const
virtual void connectNodes(const Node &nTail, const Node &nHead, Edge &e)
static std::string toString(const T &source)
DependenceType dependenceType() const
EdgeReason edgeReason() const
int latestCycle(const MoveNode &moveNode, unsigned int ii=UINT_MAX, bool ignoreRegAntideps=false, bool ignoreUnscheduledSuccessors=true, bool ignoreGuards=false, bool ignoreFUDeps=false, bool ignoreSameOperationEdges=false) const
NodeSet regRawSuccessors(const MoveNode &node) const
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
void copyOutgoingGuardWarEdges(const MoveNode &src, MoveNode &dst)
NodeSet unscheduledMoves() const
DataDependenceGraph * criticalPathGraph()
NodeSet movesAtCycle(int cycle) const
bool exclusingGuards(const MoveNode &mn1, const MoveNode &mn2) const
MoveNode * lastScheduledRegisterRead(const TTAMachine::BaseRegisterFile &rf, int registerIndex, int lastCycleToTest=INT_MAX) const
MoveNode * findLimitingAntidependenceSource(MoveNode &mn)
void copyIncomingGuardEdges(const MoveNode &src, MoveNode &dst)
std::pair< MoveNode *, MoveNode * > findLoopLimitAndIndex(MoveNode &jumpMove)
virtual void setCycleGrouping(bool flag)
void writeToXMLFile(std::string fileName) const
void removeOutgoingGuardWarEdges(MoveNode &node)
NodeSet guardDefMoves(const MoveNode &moveNode)
EdgeSet copyDepsOver(MoveNode &node, bool anti, bool raw)
void removeIncomingGuardEdges(MoveNode &node)
std::string errorMessageStack(bool messagesOnly=false) const
Definition Exception.cc:138
virtual void writeToDotFile(const TCEString &fileName) const
std::set< GraphNode *, typename GraphNode::Comparator > NodeSet
Definition Graph.hh:53
std::set< GraphEdge *, typename GraphEdge::Comparator > EdgeSet
Definition Graph.hh:54
virtual bool dumpDDGsDot() const
virtual bool dumpDDGsXML() const
static bool containsKey(const MapType &aMap, const KeyType &aKey)
int nodeCount() const
MoveNode & node(int index) const
void addNode(MoveNode &node)
std::string toString() const
bool isScheduled() const
virtual void notifyScheduled(MoveNode &node)=0
void addMoveNode(MoveNode &)
MoveNode & at(int index)
int earliestResultReadCycle() const
Definition MoveNode.cc:652
bool isOperationMove() const
Definition MoveNode.cc:253
int cycle() const
Definition MoveNode.cc:421
bool isMove() const
int guardLatency() const
Definition MoveNode.cc:779
ProgramOperation & sourceOperation() const
Definition MoveNode.cc:453
bool isDestinationOperation() const
std::string toString() const
Definition MoveNode.cc:576
bool isPlaced() const
Definition MoveNode.cc:352
TTAProgram::Move & move()
bool isSourceOperation() const
Definition MoveNode.cc:168
bool isScheduled() const
Definition MoveNode.cc:409
bool isSourceConstant() const
Definition MoveNode.cc:238
ProgramOperation & destinationOperation(unsigned int index=0) const
virtual TCEString name() const
Definition Operation.cc:93
virtual bool hasSideEffects() const
Definition Operation.cc:272
virtual int affectsCount() const
Definition Operation.cc:402
virtual bool writesMemory() const
Definition Operation.cc:252
virtual int numberOfInputs() const
Definition Operation.cc:192
virtual bool canSwap(int id1, int id2) const
Definition Operation.cc:470
const Operation & operation() const
int inputMoveCount() const
MoveNode * triggeringMove() const
MoveNodeSet & inputNode(int in) const
MoveNode & inputMove(int index) const
void switchInputs(int idx1=1, int idx2=2)
void operandsScheduled(AddedRegisterCopies &copies, DataDependenceGraph &ddg)
AddedRegisterCopies addRegisterCopiesToRRMove(MoveNode &moveNode, DataDependenceGraph *ddg)
void resultsScheduled(AddedRegisterCopies &copies, DataDependenceGraph &ddg)
AddedRegisterCopies addMinimumRegisterCopies(ProgramOperation &programOperation, const TTAMachine::Machine &targetMachine, DataDependenceGraph *ddg)
bool renameDestinationRegister(MoveNode &node, bool loopScheduling, bool allowSameRf, bool differentRfOnlyDirectlyReachable, int earliestCycle=-1)
void setSelector(MoveNodeSelector *selector)
bool renameSourceRegister(MoveNode &node, bool loopScheduling, bool allowSameRf, bool differentRfOnlyDirectlyReachable, int latestCycle=INT_MAX)
void initialize(DataDependenceGraph &ddg)
InterPassData & interPassData()
unsigned int unsignedValue() const
Definition SimValue.cc:919
int width() const
Definition SimValue.cc:103
virtual void assign(int cycle, MoveNode &node, const TTAMachine::Bus *bus=NULL, const TTAMachine::FunctionUnit *srcFU=NULL, const TTAMachine::FunctionUnit *dstFU=NULL, int immWriteCycle=-1, const TTAMachine::ImmediateUnit *immu=nullptr, int immRegIndex=-1) override
virtual int earliestCycle(MoveNode &node, const TTAMachine::Bus *bus=NULL, const TTAMachine::FunctionUnit *srcFU=NULL, const TTAMachine::FunctionUnit *dstFU=NULL, int immWriteCycle=-1, const TTAMachine::ImmediateUnit *immu=nullptr, int immRegIndex=-1) const override
virtual unsigned initiationInterval() const
virtual bool canTransportImmediate(const MoveNode &node, const TTAMachine::Bus *preAssignedBus=NULL) const
virtual void unassign(MoveNode &node) override
virtual TTAProgram::Instruction * instruction(int cycle) const override
virtual int largestCycle() const override
virtual int latestCycle(MoveNode &node, const TTAMachine::Bus *bus=NULL, const TTAMachine::FunctionUnit *srcFU=NULL, const TTAMachine::FunctionUnit *dstFU=NULL, int immWriteCycle=-1, const TTAMachine::ImmediateUnit *immu=nullptr, int immRegIndex=-1) const override
virtual int removeDeadResults(MoveNodeGroup &candidates, DataDependenceGraph &ddg, ResourceManager &rm, std::set< std::pair< TTAProgram::Move *, int > > &removedMoves)=0
virtual void setSelector(MoveNodeSelector *selector)
virtual int bypassNode(MoveNode &moveNode, int &lastOperandCycle, DataDependenceGraph &ddg, ResourceManager &rm)=0
virtual void clearCaches(DataDependenceGraph &ddg, bool removeDeadResults)=0
virtual void removeBypass(MoveNodeGroup &candidates, DataDependenceGraph &ddg, ResourceManager &rm)=0
virtual int bypass(MoveNodeGroup &candidates, DataDependenceGraph &ddg, ResourceManager &rm, bool bypassTrigger)=0
virtual bool isTriggering() const
Definition FUPort.cc:182
virtual HWOperation * operation(const std::string &name) const
virtual bool hasOperation(const std::string &name) const
virtual BaseFUPort * port(const std::string &name) const
virtual FUPort * port(int operand) const
int io(const FUPort &port) const
ComponentType * item(int index) const
virtual FunctionUnitNavigator functionUnitNavigator() const
Definition Machine.cc:380
virtual ControlUnit * controlUnit() const
Definition Machine.cc:345
virtual int portCount() const
Definition Unit.cc:135
void removeAnnotations(ProgramAnnotation::Id id=ProgramAnnotation::ANN_UNDEF_ID)
void setAnnotation(const ProgramAnnotation &annotation)
bool hasAnnotations(ProgramAnnotation::Id id=ProgramAnnotation::ANN_UNDEF_ID) const
static TTAProgram::MoveGuard * createInverseGuard(const TTAProgram::MoveGuard &mg, const TTAMachine::Bus *bus=NULL)
std::string toString() const
void setSource(Terminal *src)
Definition Move.cc:312
MoveGuard & guard() const
Definition Move.cc:345
bool isControlFlowMove() const
Definition Move.cc:233
bool isReturn() const
Definition Move.cc:259
bool isUnconditional() const
Definition Move.cc:154
std::string toString() const
Definition Move.cc:436
Terminal & source() const
Definition Move.cc:302
void setGuard(MoveGuard *guard)
Definition Move.cc:360
bool isTriggering() const
Definition Move.cc:284
Terminal & destination() const
Definition Move.cc:323
@ ANN_STACKFRAME_PROCEDURE_RETURN
precedure return jmp
@ ANN_CONNECTIVITY_MOVE
A reg to reg move that was added because of missing connectivity between the original target and dest...
virtual SimValue value() const
virtual bool isRA() const
Definition Terminal.cc:129
virtual bool isTriggering() const
Definition Terminal.cc:298
virtual int index() const
Definition Terminal.cc:274
virtual bool equals(const Terminal &other) const =0
virtual bool isGPR() const
Definition Terminal.cc:107
virtual int operationIndex() const
Definition Terminal.cc:364
virtual const TTAMachine::RegisterFile & registerFile() const
Definition Terminal.cc:225