summaryrefslogtreecommitdiff
path: root/lib/CodeGen/InstrSched/SchedPriorities.cpp
blob: 0aaece228d8231bfbcdf0d3dd6517a8e7cdac352 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
//===-- SchedPriorities.h - Encapsulate scheduling heuristics -------------===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
// 
//===----------------------------------------------------------------------===//
// 
// Strategy:
//    Priority ordering rules:
//    (1) Max delay, which is the order of the heap S.candsAsHeap.
//    (2) Instruction that frees up a register.
//    (3) Instruction that has the maximum number of dependent instructions.
//    Note that rules 2 and 3 are only used if issue conflicts prevent
//    choosing a higher priority instruction by rule 1.
//
//===----------------------------------------------------------------------===//

#include "SchedPriorities.h"
#include "../../Target/SparcV9/LiveVar/FunctionLiveVarInfo.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/Support/CFG.h"
#include "llvm/ADT/PostOrderIterator.h"
#include <iostream>

namespace llvm {

std::ostream &operator<<(std::ostream &os, const NodeDelayPair* nd) {
  return os << "Delay for node " << nd->node->getNodeId()
	    << " = " << (long)nd->delay << "\n";
}


SchedPriorities::SchedPriorities(const Function *, const SchedGraph *G,
                                 FunctionLiveVarInfo &LVI)
  : curTime(0), graph(G), methodLiveVarInfo(LVI),
    nodeDelayVec(G->getNumNodes(), INVALID_LATENCY), // make errors obvious
    earliestReadyTimeForNode(G->getNumNodes(), 0),
    earliestReadyTime(0),
    nextToTry(candsAsHeap.begin())
{
  computeDelays(graph);
}


void
SchedPriorities::initialize() {
  initializeReadyHeap(graph);
}


void
SchedPriorities::computeDelays(const SchedGraph* graph) {
  po_iterator<const SchedGraph*> poIter = po_begin(graph), poEnd =po_end(graph);
  for ( ; poIter != poEnd; ++poIter) {
    const SchedGraphNode* node = *poIter;
    cycles_t nodeDelay;
    if (node->beginOutEdges() == node->endOutEdges())
      nodeDelay = node->getLatency();
    else {
      // Iterate over the out-edges of the node to compute delay
      nodeDelay = 0;
      for (SchedGraphNode::const_iterator E=node->beginOutEdges();
           E != node->endOutEdges(); ++E) {
        cycles_t sinkDelay = getNodeDelay((SchedGraphNode*)(*E)->getSink());
        nodeDelay = std::max(nodeDelay, sinkDelay + (*E)->getMinDelay());
      }
    }
    getNodeDelayRef(node) = nodeDelay;
  }
}


void
SchedPriorities::initializeReadyHeap(const SchedGraph* graph) {
  const SchedGraphNode* graphRoot = (const SchedGraphNode*)graph->getRoot();
  assert(graphRoot->getMachineInstr() == NULL && "Expect dummy root");
  
  // Insert immediate successors of dummy root, which are the actual roots
  sg_succ_const_iterator SEnd = succ_end(graphRoot);
  for (sg_succ_const_iterator S = succ_begin(graphRoot); S != SEnd; ++S)
    this->insertReady(*S);
  
#undef TEST_HEAP_CONVERSION
#ifdef TEST_HEAP_CONVERSION
  std::cerr << "Before heap conversion:\n";
  copy(candsAsHeap.begin(), candsAsHeap.end(),
       ostream_iterator<NodeDelayPair*>(std::cerr,"\n"));
#endif
  
  candsAsHeap.makeHeap();
  
  nextToTry = candsAsHeap.begin();
  
#ifdef TEST_HEAP_CONVERSION
  std::cerr << "After heap conversion:\n";
  copy(candsAsHeap.begin(), candsAsHeap.end(),
       ostream_iterator<NodeDelayPair*>(std::cerr,"\n"));
#endif
}

void
SchedPriorities::insertReady(const SchedGraphNode* node) {
  candsAsHeap.insert(node, nodeDelayVec[node->getNodeId()]);
  candsAsSet.insert(node);
  mcands.clear(); // ensure reset choices is called before any more choices
  earliestReadyTime = std::min(earliestReadyTime,
                       getEarliestReadyTimeForNode(node));
  
  if (SchedDebugLevel >= Sched_PrintSchedTrace) {
    std::cerr << " Node " << node->getNodeId() << " will be ready in Cycle "
              << getEarliestReadyTimeForNode(node) << "; "
              << " Delay = " <<(long)getNodeDelay(node) << "; Instruction: \n"
              << "        " << *node->getMachineInstr() << "\n";
  }
}

void
SchedPriorities::issuedReadyNodeAt(cycles_t curTime,
				   const SchedGraphNode* node) {
  candsAsHeap.removeNode(node);
  candsAsSet.erase(node);
  mcands.clear(); // ensure reset choices is called before any more choices
  
  if (earliestReadyTime == getEarliestReadyTimeForNode(node)) {
    // earliestReadyTime may have been due to this node, so recompute it
    earliestReadyTime = HUGE_LATENCY;
    for (NodeHeap::const_iterator I=candsAsHeap.begin();
         I != candsAsHeap.end(); ++I)
      if (candsAsHeap.getNode(I)) {
        earliestReadyTime = 
          std::min(earliestReadyTime, 
                   getEarliestReadyTimeForNode(candsAsHeap.getNode(I)));
      }
  }
  
  // Now update ready times for successors
  for (SchedGraphNode::const_iterator E=node->beginOutEdges();
       E != node->endOutEdges(); ++E) {
    cycles_t& etime =
      getEarliestReadyTimeForNodeRef((SchedGraphNode*)(*E)->getSink());
    etime = std::max(etime, curTime + (*E)->getMinDelay());
  }    
}


//----------------------------------------------------------------------
// Priority ordering rules:
// (1) Max delay, which is the order of the heap S.candsAsHeap.
// (2) Instruction that frees up a register.
// (3) Instruction that has the maximum number of dependent instructions.
// Note that rules 2 and 3 are only used if issue conflicts prevent
// choosing a higher priority instruction by rule 1.
//----------------------------------------------------------------------

inline int
SchedPriorities::chooseByRule1(std::vector<candIndex>& mcands) {
  return (mcands.size() == 1)? 0	// only one choice exists so take it
			     : -1;	// -1 indicates multiple choices
}

inline int
SchedPriorities::chooseByRule2(std::vector<candIndex>& mcands) {
  assert(mcands.size() >= 1 && "Should have at least one candidate here.");
  for (unsigned i=0, N = mcands.size(); i < N; i++)
    if (instructionHasLastUse(methodLiveVarInfo,
			      candsAsHeap.getNode(mcands[i])))
      return i;
  return -1;
}

inline int
SchedPriorities::chooseByRule3(std::vector<candIndex>& mcands) {
  assert(mcands.size() >= 1 && "Should have at least one candidate here.");
  int maxUses = candsAsHeap.getNode(mcands[0])->getNumOutEdges();	
  int indexWithMaxUses = 0;
  for (unsigned i=1, N = mcands.size(); i < N; i++) {
    int numUses = candsAsHeap.getNode(mcands[i])->getNumOutEdges();
    if (numUses > maxUses) {
      maxUses = numUses;
      indexWithMaxUses = i;
    }
  }
  return indexWithMaxUses; 
}

const SchedGraphNode*
SchedPriorities::getNextHighest(const SchedulingManager& S,
				cycles_t curTime) {
  int nextIdx = -1;
  const SchedGraphNode* nextChoice = NULL;
  
  if (mcands.size() == 0)
    findSetWithMaxDelay(mcands, S);
  
  while (nextIdx < 0 && mcands.size() > 0) {
    nextIdx = chooseByRule1(mcands);	 // rule 1
      
    if (nextIdx == -1)
      nextIdx = chooseByRule2(mcands); // rule 2
      
    if (nextIdx == -1)
      nextIdx = chooseByRule3(mcands); // rule 3
      
    if (nextIdx == -1)
      nextIdx = 0;			 // default to first choice by delays
      
    // We have found the next best candidate.  Check if it ready in
    // the current cycle, and if it is feasible.
    // If not, remove it from mcands and continue.  Refill mcands if
    // it becomes empty.
    nextChoice = candsAsHeap.getNode(mcands[nextIdx]);
    if (getEarliestReadyTimeForNode(nextChoice) > curTime
        || ! instrIsFeasible(S, nextChoice->getMachineInstr()->getOpcode()))
    {
      mcands.erase(mcands.begin() + nextIdx);
      nextIdx = -1;
      if (mcands.size() == 0)
        findSetWithMaxDelay(mcands, S);
    }
  }
  
  if (nextIdx >= 0) {
    mcands.erase(mcands.begin() + nextIdx);
    return nextChoice;
  } else
    return NULL;
}


void
SchedPriorities::findSetWithMaxDelay(std::vector<candIndex>& mcands,
				     const SchedulingManager& S)
{
  if (mcands.size() == 0 && nextToTry != candsAsHeap.end())
    { // out of choices at current maximum delay;
      // put nodes with next highest delay in mcands
      candIndex next = nextToTry;
      cycles_t maxDelay = candsAsHeap.getDelay(next);
      for (; next != candsAsHeap.end()
	     && candsAsHeap.getDelay(next) == maxDelay; ++next)
	mcands.push_back(next);
      
      nextToTry = next;
      
      if (SchedDebugLevel >= Sched_PrintSchedTrace) {
        std::cerr << "    Cycle " << (long)getTime() << ": "
                  << "Next highest delay = " << (long)maxDelay << " : "
                  << mcands.size() << " Nodes with this delay: ";
        for (unsigned i=0; i < mcands.size(); i++)
          std::cerr << candsAsHeap.getNode(mcands[i])->getNodeId() << ", ";
        std::cerr << "\n";
      }
    }
}


bool
SchedPriorities::instructionHasLastUse(FunctionLiveVarInfo &LVI,
				       const SchedGraphNode* graphNode) {
  const MachineInstr *MI = graphNode->getMachineInstr();
  
  hash_map<const MachineInstr*, bool>::const_iterator
    ui = lastUseMap.find(MI);
  if (ui != lastUseMap.end())
    return ui->second;
  
  // else check if instruction is a last use and save it in the hash_map
  bool hasLastUse = false;
  const BasicBlock* bb = graphNode->getMachineBasicBlock().getBasicBlock();
  const ValueSet &LVs = LVI.getLiveVarSetBeforeMInst(MI, bb);
  
  for (MachineInstr::const_val_op_iterator OI = MI->begin(), OE = MI->end();
       OI != OE; ++OI)
    if (!LVs.count(*OI)) {
      hasLastUse = true;
      break;
    }

  return lastUseMap[MI] = hasLastUse;
}

} // End llvm namespace