mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-14 11:32:34 +00:00
Refactor scheduler code. Move register-reduction list scheduler to a
separate file. Added an initial implementation of top-down register pressure reduction list scheduler. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@28226 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
e993cc27ad
commit
e165a78551
@ -17,6 +17,8 @@
|
||||
|
||||
#include "llvm/CodeGen/SelectionDAG.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace llvm {
|
||||
struct InstrStage;
|
||||
class MachineConstantPool;
|
||||
@ -71,8 +73,88 @@ namespace llvm {
|
||||
}
|
||||
};
|
||||
|
||||
/// SUnit - Scheduling unit. It's an wrapper around either a single SDNode or
|
||||
/// a group of nodes flagged together.
|
||||
struct SUnit {
|
||||
SDNode *Node; // Representative node.
|
||||
std::vector<SDNode*> FlaggedNodes; // All nodes flagged to Node.
|
||||
|
||||
// Preds/Succs - The SUnits before/after us in the graph. The boolean value
|
||||
// is true if the edge is a token chain edge, false if it is a value edge.
|
||||
std::set<std::pair<SUnit*,bool> > Preds; // All sunit predecessors.
|
||||
std::set<std::pair<SUnit*,bool> > Succs; // All sunit successors.
|
||||
|
||||
short NumPreds; // # of preds.
|
||||
short NumSuccs; // # of sucss.
|
||||
short NumPredsLeft; // # of preds not scheduled.
|
||||
short NumSuccsLeft; // # of succs not scheduled.
|
||||
short NumChainPredsLeft; // # of chain preds not scheduled.
|
||||
short NumChainSuccsLeft; // # of chain succs not scheduled.
|
||||
bool isTwoAddress : 1; // Is a two-address instruction.
|
||||
bool isDefNUseOperand : 1; // Is a def&use operand.
|
||||
bool isPending : 1; // True once pending.
|
||||
bool isAvailable : 1; // True once available.
|
||||
bool isScheduled : 1; // True once scheduled.
|
||||
unsigned short Latency; // Node latency.
|
||||
unsigned CycleBound; // Upper/lower cycle to be scheduled at.
|
||||
unsigned Cycle; // Once scheduled, the cycle of the op.
|
||||
unsigned Depth; // Node depth;
|
||||
unsigned Height; // Node height;
|
||||
unsigned NodeNum; // Entry # of node in the node vector.
|
||||
|
||||
SUnit(SDNode *node, unsigned nodenum)
|
||||
: Node(node), NumPreds(0), NumSuccs(0), NumPredsLeft(0), NumSuccsLeft(0),
|
||||
NumChainPredsLeft(0), NumChainSuccsLeft(0),
|
||||
isTwoAddress(false), isDefNUseOperand(false),
|
||||
isPending(false), isAvailable(false), isScheduled(false),
|
||||
Latency(0), CycleBound(0), Cycle(0), Depth(0), Height(0),
|
||||
NodeNum(nodenum) {}
|
||||
|
||||
void dump(const SelectionDAG *G) const;
|
||||
void dumpAll(const SelectionDAG *G) const;
|
||||
};
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
/// SchedulingPriorityQueue - This interface is used to plug different
|
||||
/// priorities computation algorithms into the list scheduler. It implements
|
||||
/// the interface of a standard priority queue, where nodes are inserted in
|
||||
/// arbitrary order and returned in priority order. The computation of the
|
||||
/// priority and the representation of the queue are totally up to the
|
||||
/// implementation to decide.
|
||||
///
|
||||
class SchedulingPriorityQueue {
|
||||
public:
|
||||
virtual ~SchedulingPriorityQueue() {}
|
||||
|
||||
virtual void initNodes(const std::vector<SUnit> &SUnits) = 0;
|
||||
virtual void releaseState() = 0;
|
||||
|
||||
virtual bool empty() const = 0;
|
||||
virtual void push(SUnit *U) = 0;
|
||||
|
||||
virtual void push_all(const std::vector<SUnit *> &Nodes) = 0;
|
||||
virtual SUnit *pop() = 0;
|
||||
|
||||
/// ScheduledNode - As each node is scheduled, this method is invoked. This
|
||||
/// allows the priority function to adjust the priority of node that have
|
||||
/// already been emitted.
|
||||
virtual void ScheduledNode(SUnit *Node) {}
|
||||
};
|
||||
|
||||
class ScheduleDAG {
|
||||
public:
|
||||
|
||||
// Scheduling heuristics
|
||||
enum SchedHeuristics {
|
||||
defaultScheduling, // Let the target specify its preference.
|
||||
noScheduling, // No scheduling, emit breadth first sequence.
|
||||
simpleScheduling, // Two pass, min. critical path, max. utilization.
|
||||
simpleNoItinScheduling, // Same as above exact using generic latency.
|
||||
listSchedulingBURR, // Bottom-up reg reduction list scheduling.
|
||||
listSchedulingTDRR, // Top-down reg reduction list scheduling.
|
||||
listSchedulingTD // Top-down list scheduler.
|
||||
};
|
||||
|
||||
SelectionDAG &DAG; // DAG of the current basic block
|
||||
MachineBasicBlock *BB; // Current basic block
|
||||
const TargetMachine &TM; // Target processor
|
||||
@ -80,6 +162,10 @@ namespace llvm {
|
||||
const MRegisterInfo *MRI; // Target processor register info
|
||||
SSARegMap *RegMap; // Virtual/real register map
|
||||
MachineConstantPool *ConstPool; // Target constant pool
|
||||
std::vector<SUnit*> Sequence; // The schedule. Null SUnit*'s represent
|
||||
// noop instructions.
|
||||
std::map<SDNode*, SUnit*> SUnitMap; // SDNode to SUnit mapping (n -> 1).
|
||||
std::vector<SUnit> SUnits; // The scheduling units.
|
||||
|
||||
ScheduleDAG(SelectionDAG &dag, MachineBasicBlock *bb,
|
||||
const TargetMachine &tm)
|
||||
@ -105,6 +191,23 @@ namespace llvm {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// NewSUnit - Creates a new SUnit and return a ptr to it.
|
||||
///
|
||||
SUnit *NewSUnit(SDNode *N) {
|
||||
SUnits.push_back(SUnit(N, SUnits.size()));
|
||||
return &SUnits.back();
|
||||
}
|
||||
|
||||
/// BuildSchedUnits - Build SUnits from the selection dag that we are input.
|
||||
/// This SUnit graph is similar to the SelectionDAG, but represents flagged
|
||||
/// together nodes with a single SUnit.
|
||||
void BuildSchedUnits();
|
||||
|
||||
/// CalculateDepths, CalculateHeights - Calculate node depth / height.
|
||||
///
|
||||
void CalculateDepths();
|
||||
void CalculateHeights();
|
||||
|
||||
/// EmitNode - Generate machine code for an node and needed dependencies.
|
||||
/// VRBaseMap contains, for each already emitted node, the first virtual
|
||||
/// register number for the results of the node.
|
||||
@ -115,6 +218,9 @@ namespace llvm {
|
||||
///
|
||||
void EmitNoop();
|
||||
|
||||
void EmitSchedule();
|
||||
|
||||
void dumpSchedule() const;
|
||||
|
||||
/// Schedule - Order nodes according to selected style.
|
||||
///
|
||||
@ -138,6 +244,11 @@ namespace llvm {
|
||||
ScheduleDAG* createBURRListDAGScheduler(SelectionDAG &DAG,
|
||||
MachineBasicBlock *BB);
|
||||
|
||||
/// createTDRRListDAGScheduler - This creates a top down register usage
|
||||
/// reduction list scheduler.
|
||||
ScheduleDAG* createTDRRListDAGScheduler(SelectionDAG &DAG,
|
||||
MachineBasicBlock *BB);
|
||||
|
||||
/// createTDListDAGScheduler - This creates a top-down list scheduler with
|
||||
/// the specified hazard recognizer. This takes ownership of the hazard
|
||||
/// recognizer and deletes it when done.
|
||||
|
@ -13,6 +13,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "sched"
|
||||
#include "llvm/CodeGen/ScheduleDAG.h"
|
||||
#include "llvm/CodeGen/MachineConstantPool.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
@ -20,10 +21,185 @@
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Target/TargetLowering.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include <iostream>
|
||||
using namespace llvm;
|
||||
|
||||
|
||||
/// BuildSchedUnits - Build SUnits from the selection dag that we are input.
|
||||
/// This SUnit graph is similar to the SelectionDAG, but represents flagged
|
||||
/// together nodes with a single SUnit.
|
||||
void ScheduleDAG::BuildSchedUnits() {
|
||||
// Reserve entries in the vector for each of the SUnits we are creating. This
|
||||
// ensure that reallocation of the vector won't happen, so SUnit*'s won't get
|
||||
// invalidated.
|
||||
SUnits.reserve(std::distance(DAG.allnodes_begin(), DAG.allnodes_end()));
|
||||
|
||||
const InstrItineraryData &InstrItins = TM.getInstrItineraryData();
|
||||
|
||||
for (SelectionDAG::allnodes_iterator NI = DAG.allnodes_begin(),
|
||||
E = DAG.allnodes_end(); NI != E; ++NI) {
|
||||
if (isPassiveNode(NI)) // Leaf node, e.g. a TargetImmediate.
|
||||
continue;
|
||||
|
||||
// If this node has already been processed, stop now.
|
||||
if (SUnitMap[NI]) continue;
|
||||
|
||||
SUnit *NodeSUnit = NewSUnit(NI);
|
||||
|
||||
// See if anything is flagged to this node, if so, add them to flagged
|
||||
// nodes. Nodes can have at most one flag input and one flag output. Flags
|
||||
// are required the be the last operand and result of a node.
|
||||
|
||||
// Scan up, adding flagged preds to FlaggedNodes.
|
||||
SDNode *N = NI;
|
||||
while (N->getNumOperands() &&
|
||||
N->getOperand(N->getNumOperands()-1).getValueType() == MVT::Flag) {
|
||||
N = N->getOperand(N->getNumOperands()-1).Val;
|
||||
NodeSUnit->FlaggedNodes.push_back(N);
|
||||
SUnitMap[N] = NodeSUnit;
|
||||
}
|
||||
|
||||
// Scan down, adding this node and any flagged succs to FlaggedNodes if they
|
||||
// have a user of the flag operand.
|
||||
N = NI;
|
||||
while (N->getValueType(N->getNumValues()-1) == MVT::Flag) {
|
||||
SDOperand FlagVal(N, N->getNumValues()-1);
|
||||
|
||||
// There are either zero or one users of the Flag result.
|
||||
bool HasFlagUse = false;
|
||||
for (SDNode::use_iterator UI = N->use_begin(), E = N->use_end();
|
||||
UI != E; ++UI)
|
||||
if (FlagVal.isOperand(*UI)) {
|
||||
HasFlagUse = true;
|
||||
NodeSUnit->FlaggedNodes.push_back(N);
|
||||
SUnitMap[N] = NodeSUnit;
|
||||
N = *UI;
|
||||
break;
|
||||
}
|
||||
if (!HasFlagUse) break;
|
||||
}
|
||||
|
||||
// Now all flagged nodes are in FlaggedNodes and N is the bottom-most node.
|
||||
// Update the SUnit
|
||||
NodeSUnit->Node = N;
|
||||
SUnitMap[N] = NodeSUnit;
|
||||
|
||||
// Compute the latency for the node. We use the sum of the latencies for
|
||||
// all nodes flagged together into this SUnit.
|
||||
if (InstrItins.isEmpty()) {
|
||||
// No latency information.
|
||||
NodeSUnit->Latency = 1;
|
||||
} else {
|
||||
NodeSUnit->Latency = 0;
|
||||
if (N->isTargetOpcode()) {
|
||||
unsigned SchedClass = TII->getSchedClass(N->getTargetOpcode());
|
||||
InstrStage *S = InstrItins.begin(SchedClass);
|
||||
InstrStage *E = InstrItins.end(SchedClass);
|
||||
for (; S != E; ++S)
|
||||
NodeSUnit->Latency += S->Cycles;
|
||||
}
|
||||
for (unsigned i = 0, e = NodeSUnit->FlaggedNodes.size(); i != e; ++i) {
|
||||
SDNode *FNode = NodeSUnit->FlaggedNodes[i];
|
||||
if (FNode->isTargetOpcode()) {
|
||||
unsigned SchedClass = TII->getSchedClass(FNode->getTargetOpcode());
|
||||
InstrStage *S = InstrItins.begin(SchedClass);
|
||||
InstrStage *E = InstrItins.end(SchedClass);
|
||||
for (; S != E; ++S)
|
||||
NodeSUnit->Latency += S->Cycles;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: add the preds, succs, etc.
|
||||
for (unsigned su = 0, e = SUnits.size(); su != e; ++su) {
|
||||
SUnit *SU = &SUnits[su];
|
||||
SDNode *MainNode = SU->Node;
|
||||
|
||||
if (MainNode->isTargetOpcode()) {
|
||||
unsigned Opc = MainNode->getTargetOpcode();
|
||||
if (TII->isTwoAddrInstr(Opc)) {
|
||||
SU->isTwoAddress = true;
|
||||
SDNode *OpN = MainNode->getOperand(0).Val;
|
||||
SUnit *OpSU = SUnitMap[OpN];
|
||||
if (OpSU)
|
||||
OpSU->isDefNUseOperand = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find all predecessors and successors of the group.
|
||||
// Temporarily add N to make code simpler.
|
||||
SU->FlaggedNodes.push_back(MainNode);
|
||||
|
||||
for (unsigned n = 0, e = SU->FlaggedNodes.size(); n != e; ++n) {
|
||||
SDNode *N = SU->FlaggedNodes[n];
|
||||
|
||||
for (unsigned i = 0, e = N->getNumOperands(); i != e; ++i) {
|
||||
SDNode *OpN = N->getOperand(i).Val;
|
||||
if (isPassiveNode(OpN)) continue; // Not scheduled.
|
||||
SUnit *OpSU = SUnitMap[OpN];
|
||||
assert(OpSU && "Node has no SUnit!");
|
||||
if (OpSU == SU) continue; // In the same group.
|
||||
|
||||
MVT::ValueType OpVT = N->getOperand(i).getValueType();
|
||||
assert(OpVT != MVT::Flag && "Flagged nodes should be in same sunit!");
|
||||
bool isChain = OpVT == MVT::Other;
|
||||
|
||||
if (SU->Preds.insert(std::make_pair(OpSU, isChain)).second) {
|
||||
if (!isChain) {
|
||||
SU->NumPreds++;
|
||||
SU->NumPredsLeft++;
|
||||
} else {
|
||||
SU->NumChainPredsLeft++;
|
||||
}
|
||||
}
|
||||
if (OpSU->Succs.insert(std::make_pair(SU, isChain)).second) {
|
||||
if (!isChain) {
|
||||
OpSU->NumSuccs++;
|
||||
OpSU->NumSuccsLeft++;
|
||||
} else {
|
||||
OpSU->NumChainSuccsLeft++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove MainNode from FlaggedNodes again.
|
||||
SU->FlaggedNodes.pop_back();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void CalculateDepths(SUnit *SU, unsigned Depth) {
|
||||
if (Depth > SU->Depth) SU->Depth = Depth;
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator I = SU->Succs.begin(),
|
||||
E = SU->Succs.end(); I != E; ++I)
|
||||
CalculateDepths(I->first, Depth+1);
|
||||
}
|
||||
|
||||
void ScheduleDAG::CalculateDepths() {
|
||||
SUnit *Entry = SUnitMap[DAG.getEntryNode().Val];
|
||||
::CalculateDepths(Entry, 0U);
|
||||
for (unsigned i = 0, e = SUnits.size(); i != e; ++i)
|
||||
if (SUnits[i].Preds.size() == 0 && &SUnits[i] != Entry) {
|
||||
::CalculateDepths(&SUnits[i], 0U);
|
||||
}
|
||||
}
|
||||
|
||||
static void CalculateHeights(SUnit *SU, unsigned Height) {
|
||||
if (Height > SU->Height) SU->Height = Height;
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator I = SU->Preds.begin(),
|
||||
E = SU->Preds.end(); I != E; ++I)
|
||||
CalculateHeights(I->first, Height+1);
|
||||
}
|
||||
void ScheduleDAG::CalculateHeights() {
|
||||
SUnit *Root = SUnitMap[DAG.getRoot().Val];
|
||||
::CalculateHeights(Root, 0U);
|
||||
}
|
||||
|
||||
/// CountResults - The results of target nodes have register or immediate
|
||||
/// operands first, then an optional chain, and optional flag operands (which do
|
||||
/// not go into the machine instrs.)
|
||||
@ -348,6 +524,32 @@ void ScheduleDAG::EmitNoop() {
|
||||
TII->insertNoop(*BB, BB->end());
|
||||
}
|
||||
|
||||
/// EmitSchedule - Emit the machine code in scheduled order.
|
||||
void ScheduleDAG::EmitSchedule() {
|
||||
std::map<SDNode*, unsigned> VRBaseMap;
|
||||
for (unsigned i = 0, e = Sequence.size(); i != e; i++) {
|
||||
if (SUnit *SU = Sequence[i]) {
|
||||
for (unsigned j = 0, ee = SU->FlaggedNodes.size(); j != ee; j++)
|
||||
EmitNode(SU->FlaggedNodes[j], VRBaseMap);
|
||||
EmitNode(SU->Node, VRBaseMap);
|
||||
} else {
|
||||
// Null SUnit* is a noop.
|
||||
EmitNoop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// dump - dump the schedule.
|
||||
void ScheduleDAG::dumpSchedule() const {
|
||||
for (unsigned i = 0, e = Sequence.size(); i != e; i++) {
|
||||
if (SUnit *SU = Sequence[i])
|
||||
SU->dump(&DAG);
|
||||
else
|
||||
std::cerr << "**** NOOP ****\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Run - perform scheduling.
|
||||
///
|
||||
MachineBasicBlock *ScheduleDAG::Run() {
|
||||
@ -360,4 +562,53 @@ MachineBasicBlock *ScheduleDAG::Run() {
|
||||
return BB;
|
||||
}
|
||||
|
||||
/// SUnit - Scheduling unit. It's an wrapper around either a single SDNode or
|
||||
/// a group of nodes flagged together.
|
||||
void SUnit::dump(const SelectionDAG *G) const {
|
||||
std::cerr << "SU(" << NodeNum << "): ";
|
||||
Node->dump(G);
|
||||
std::cerr << "\n";
|
||||
if (FlaggedNodes.size() != 0) {
|
||||
for (unsigned i = 0, e = FlaggedNodes.size(); i != e; i++) {
|
||||
std::cerr << " ";
|
||||
FlaggedNodes[i]->dump(G);
|
||||
std::cerr << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SUnit::dumpAll(const SelectionDAG *G) const {
|
||||
dump(G);
|
||||
|
||||
std::cerr << " # preds left : " << NumPredsLeft << "\n";
|
||||
std::cerr << " # succs left : " << NumSuccsLeft << "\n";
|
||||
std::cerr << " # chain preds left : " << NumChainPredsLeft << "\n";
|
||||
std::cerr << " # chain succs left : " << NumChainSuccsLeft << "\n";
|
||||
std::cerr << " Latency : " << Latency << "\n";
|
||||
std::cerr << " Depth : " << Depth << "\n";
|
||||
std::cerr << " Height : " << Height << "\n";
|
||||
|
||||
if (Preds.size() != 0) {
|
||||
std::cerr << " Predecessors:\n";
|
||||
for (std::set<std::pair<SUnit*,bool> >::const_iterator I = Preds.begin(),
|
||||
E = Preds.end(); I != E; ++I) {
|
||||
if (I->second)
|
||||
std::cerr << " ch ";
|
||||
else
|
||||
std::cerr << " val ";
|
||||
I->first->dump(G);
|
||||
}
|
||||
}
|
||||
if (Succs.size() != 0) {
|
||||
std::cerr << " Successors:\n";
|
||||
for (std::set<std::pair<SUnit*, bool> >::const_iterator I = Succs.begin(),
|
||||
E = Succs.end(); I != E; ++I) {
|
||||
if (I->second)
|
||||
std::cerr << " ch ";
|
||||
else
|
||||
std::cerr << " val ";
|
||||
I->first->dump(G);
|
||||
}
|
||||
}
|
||||
std::cerr << "\n";
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
813
lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp
Normal file
813
lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp
Normal file
@ -0,0 +1,813 @@
|
||||
//===----- ScheduleDAGList.cpp - Reg pressure reduction list scheduler ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file was developed by Evan Cheng and is distributed under the
|
||||
// University of Illinois Open Source License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This implements bottom-up and top-down register pressure reduction list
|
||||
// schedulers, using standard algorithms. The basic approach uses a priority
|
||||
// queue of available nodes to schedule. One at a time, nodes are taken from
|
||||
// the priority queue (thus in priority order), checked for legality to
|
||||
// schedule, and emitted if legal.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "sched"
|
||||
#include "llvm/CodeGen/ScheduleDAG.h"
|
||||
#include "llvm/CodeGen/SSARegMap.h"
|
||||
#include "llvm/Target/MRegisterInfo.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
cl::opt<bool> SchedLowerDefNUse("sched-lower-defnuse", cl::Hidden);
|
||||
}
|
||||
|
||||
namespace {
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// ScheduleDAGRRList - The actual register reduction list scheduler
|
||||
/// implementation. This supports both top-down and bottom-up scheduling.
|
||||
///
|
||||
|
||||
class ScheduleDAGRRList : public ScheduleDAG {
|
||||
private:
|
||||
/// isBottomUp - This is true if the scheduling problem is bottom-up, false if
|
||||
/// it is top-down.
|
||||
bool isBottomUp;
|
||||
|
||||
/// AvailableQueue - The priority queue to use for the available SUnits.
|
||||
///
|
||||
SchedulingPriorityQueue *AvailableQueue;
|
||||
|
||||
public:
|
||||
ScheduleDAGRRList(SelectionDAG &dag, MachineBasicBlock *bb,
|
||||
const TargetMachine &tm, bool isbottomup,
|
||||
SchedulingPriorityQueue *availqueue)
|
||||
: ScheduleDAG(dag, bb, tm), isBottomUp(isbottomup),
|
||||
AvailableQueue(availqueue) {
|
||||
}
|
||||
|
||||
~ScheduleDAGRRList() {
|
||||
delete AvailableQueue;
|
||||
}
|
||||
|
||||
void Schedule();
|
||||
|
||||
private:
|
||||
void ReleasePred(SUnit *PredSU, bool isChain, unsigned CurCycle);
|
||||
void ReleaseSucc(SUnit *SuccSU, bool isChain, unsigned CurCycle);
|
||||
void ScheduleNodeBottomUp(SUnit *SU, unsigned& CurCycle);
|
||||
void ScheduleNodeTopDown(SUnit *SU, unsigned& CurCycle);
|
||||
void ListScheduleTopDown();
|
||||
void ListScheduleBottomUp();
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
/// Schedule - Schedule the DAG using list scheduling.
|
||||
void ScheduleDAGRRList::Schedule() {
|
||||
DEBUG(std::cerr << "********** List Scheduling **********\n");
|
||||
|
||||
// Build scheduling units.
|
||||
BuildSchedUnits();
|
||||
|
||||
CalculateDepths();
|
||||
CalculateHeights();
|
||||
DEBUG(for (unsigned su = 0, e = SUnits.size(); su != e; ++su)
|
||||
SUnits[su].dumpAll(&DAG));
|
||||
|
||||
AvailableQueue->initNodes(SUnits);
|
||||
|
||||
// Execute the actual scheduling loop Top-Down or Bottom-Up as appropriate.
|
||||
if (isBottomUp)
|
||||
ListScheduleBottomUp();
|
||||
else
|
||||
ListScheduleTopDown();
|
||||
|
||||
AvailableQueue->releaseState();
|
||||
|
||||
DEBUG(std::cerr << "*** Final schedule ***\n");
|
||||
DEBUG(dumpSchedule());
|
||||
DEBUG(std::cerr << "\n");
|
||||
|
||||
// Emit in scheduled order
|
||||
EmitSchedule();
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Bottom-Up Scheduling
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static const TargetRegisterClass *getRegClass(SUnit *SU,
|
||||
const TargetInstrInfo *TII,
|
||||
const MRegisterInfo *MRI,
|
||||
SSARegMap *RegMap) {
|
||||
if (SU->Node->isTargetOpcode()) {
|
||||
unsigned Opc = SU->Node->getTargetOpcode();
|
||||
const TargetInstrDescriptor &II = TII->get(Opc);
|
||||
return II.OpInfo->RegClass;
|
||||
} else {
|
||||
assert(SU->Node->getOpcode() == ISD::CopyFromReg);
|
||||
unsigned SrcReg = cast<RegisterSDNode>(SU->Node->getOperand(1))->getReg();
|
||||
if (MRegisterInfo::isVirtualRegister(SrcReg))
|
||||
return RegMap->getRegClass(SrcReg);
|
||||
else {
|
||||
for (MRegisterInfo::regclass_iterator I = MRI->regclass_begin(),
|
||||
E = MRI->regclass_end(); I != E; ++I)
|
||||
if ((*I)->hasType(SU->Node->getValueType(0)) &&
|
||||
(*I)->contains(SrcReg))
|
||||
return *I;
|
||||
assert(false && "Couldn't find register class for reg copy!");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned getNumResults(SUnit *SU) {
|
||||
unsigned NumResults = 0;
|
||||
for (unsigned i = 0, e = SU->Node->getNumValues(); i != e; ++i) {
|
||||
MVT::ValueType VT = SU->Node->getValueType(i);
|
||||
if (VT != MVT::Other && VT != MVT::Flag)
|
||||
NumResults++;
|
||||
}
|
||||
return NumResults;
|
||||
}
|
||||
|
||||
/// ReleasePred - Decrement the NumSuccsLeft count of a predecessor. Add it to
|
||||
/// the Available queue is the count reaches zero. Also update its cycle bound.
|
||||
void ScheduleDAGRRList::ReleasePred(SUnit *PredSU, bool isChain,
|
||||
unsigned CurCycle) {
|
||||
// FIXME: the distance between two nodes is not always == the predecessor's
|
||||
// latency. For example, the reader can very well read the register written
|
||||
// by the predecessor later than the issue cycle. It also depends on the
|
||||
// interrupt model (drain vs. freeze).
|
||||
PredSU->CycleBound = std::max(PredSU->CycleBound, CurCycle + PredSU->Latency);
|
||||
|
||||
if (!isChain)
|
||||
PredSU->NumSuccsLeft--;
|
||||
else
|
||||
PredSU->NumChainSuccsLeft--;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (PredSU->NumSuccsLeft < 0 || PredSU->NumChainSuccsLeft < 0) {
|
||||
std::cerr << "*** List scheduling failed! ***\n";
|
||||
PredSU->dump(&DAG);
|
||||
std::cerr << " has been released too many times!\n";
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((PredSU->NumSuccsLeft + PredSU->NumChainSuccsLeft) == 0) {
|
||||
// EntryToken has to go last! Special case it here.
|
||||
if (PredSU->Node->getOpcode() != ISD::EntryToken) {
|
||||
PredSU->isAvailable = true;
|
||||
AvailableQueue->push(PredSU);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ScheduleNodeBottomUp - Add the node to the schedule. Decrement the pending
|
||||
/// count of its predecessors. If a predecessor pending count is zero, add it to
|
||||
/// the Available queue.
|
||||
void ScheduleDAGRRList::ScheduleNodeBottomUp(SUnit *SU, unsigned& CurCycle) {
|
||||
DEBUG(std::cerr << "*** Scheduling [" << CurCycle << "]: ");
|
||||
DEBUG(SU->dump(&DAG));
|
||||
SU->Cycle = CurCycle;
|
||||
|
||||
AvailableQueue->ScheduledNode(SU);
|
||||
Sequence.push_back(SU);
|
||||
|
||||
// Bottom up: release predecessors
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator I = SU->Preds.begin(),
|
||||
E = SU->Preds.end(); I != E; ++I)
|
||||
ReleasePred(I->first, I->second, CurCycle);
|
||||
SU->isScheduled = true;
|
||||
CurCycle++;
|
||||
}
|
||||
|
||||
/// isReady - True if node's lower cycle bound is less or equal to the current
|
||||
/// scheduling cycle. Always true if all nodes have uniform latency 1.
|
||||
static inline bool isReady(SUnit *SU, unsigned CurCycle) {
|
||||
return SU->CycleBound <= CurCycle;
|
||||
}
|
||||
|
||||
/// ListScheduleBottomUp - The main loop of list scheduling for bottom-up
|
||||
/// schedulers.
|
||||
void ScheduleDAGRRList::ListScheduleBottomUp() {
|
||||
unsigned CurCycle = 0;
|
||||
// Add root to Available queue.
|
||||
AvailableQueue->push(SUnitMap[DAG.getRoot().Val]);
|
||||
|
||||
// While Available queue is not empty, grab the node with the highest
|
||||
// priority. If it is not ready put it back. Schedule the node.
|
||||
std::vector<SUnit*> NotReady;
|
||||
SUnit *CurNode = NULL;
|
||||
while (!AvailableQueue->empty()) {
|
||||
SUnit *CurNode = AvailableQueue->pop();
|
||||
while (!isReady(CurNode, CurCycle)) {
|
||||
NotReady.push_back(CurNode);
|
||||
CurNode = AvailableQueue->pop();
|
||||
}
|
||||
|
||||
// Add the nodes that aren't ready back onto the available list.
|
||||
AvailableQueue->push_all(NotReady);
|
||||
NotReady.clear();
|
||||
|
||||
ScheduleNodeBottomUp(CurNode, CurCycle);
|
||||
}
|
||||
|
||||
// Add entry node last
|
||||
if (DAG.getEntryNode().Val != DAG.getRoot().Val) {
|
||||
SUnit *Entry = SUnitMap[DAG.getEntryNode().Val];
|
||||
Sequence.push_back(Entry);
|
||||
}
|
||||
|
||||
// Reverse the order if it is bottom up.
|
||||
std::reverse(Sequence.begin(), Sequence.end());
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Verify that all SUnits were scheduled.
|
||||
bool AnyNotSched = false;
|
||||
for (unsigned i = 0, e = SUnits.size(); i != e; ++i) {
|
||||
if (SUnits[i].NumSuccsLeft != 0 || SUnits[i].NumChainSuccsLeft != 0) {
|
||||
if (!AnyNotSched)
|
||||
std::cerr << "*** List scheduling failed! ***\n";
|
||||
SUnits[i].dump(&DAG);
|
||||
std::cerr << "has not been scheduled!\n";
|
||||
AnyNotSched = true;
|
||||
}
|
||||
}
|
||||
assert(!AnyNotSched);
|
||||
#endif
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Top-Down Scheduling
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// ReleaseSucc - Decrement the NumPredsLeft count of a successor. Add it to
|
||||
/// the PendingQueue if the count reaches zero.
|
||||
void ScheduleDAGRRList::ReleaseSucc(SUnit *SuccSU, bool isChain,
|
||||
unsigned CurCycle) {
|
||||
// FIXME: the distance between two nodes is not always == the predecessor's
|
||||
// latency. For example, the reader can very well read the register written
|
||||
// by the predecessor later than the issue cycle. It also depends on the
|
||||
// interrupt model (drain vs. freeze).
|
||||
SuccSU->CycleBound = std::max(SuccSU->CycleBound, CurCycle + SuccSU->Latency);
|
||||
|
||||
if (!isChain)
|
||||
SuccSU->NumPredsLeft--;
|
||||
else
|
||||
SuccSU->NumChainPredsLeft--;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (SuccSU->NumPredsLeft < 0 || SuccSU->NumChainPredsLeft < 0) {
|
||||
std::cerr << "*** List scheduling failed! ***\n";
|
||||
SuccSU->dump(&DAG);
|
||||
std::cerr << " has been released too many times!\n";
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((SuccSU->NumPredsLeft + SuccSU->NumChainPredsLeft) == 0) {
|
||||
SuccSU->isAvailable = true;
|
||||
AvailableQueue->push(SuccSU);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ScheduleNodeTopDown - Add the node to the schedule. Decrement the pending
|
||||
/// count of its successors. If a successor pending count is zero, add it to
|
||||
/// the Available queue.
|
||||
void ScheduleDAGRRList::ScheduleNodeTopDown(SUnit *SU, unsigned& CurCycle) {
|
||||
DEBUG(std::cerr << "*** Scheduling [" << CurCycle << "]: ");
|
||||
DEBUG(SU->dump(&DAG));
|
||||
SU->Cycle = CurCycle;
|
||||
|
||||
AvailableQueue->ScheduledNode(SU);
|
||||
Sequence.push_back(SU);
|
||||
|
||||
// Top down: release successors
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator I = SU->Succs.begin(),
|
||||
E = SU->Succs.end(); I != E; ++I)
|
||||
ReleaseSucc(I->first, I->second, CurCycle);
|
||||
SU->isScheduled = true;
|
||||
CurCycle++;
|
||||
}
|
||||
|
||||
void ScheduleDAGRRList::ListScheduleTopDown() {
|
||||
unsigned CurCycle = 0;
|
||||
SUnit *Entry = SUnitMap[DAG.getEntryNode().Val];
|
||||
|
||||
// All leaves to Available queue.
|
||||
for (unsigned i = 0, e = SUnits.size(); i != e; ++i) {
|
||||
// It is available if it has no predecessors.
|
||||
if (SUnits[i].Preds.size() == 0 && &SUnits[i] != Entry) {
|
||||
AvailableQueue->push(&SUnits[i]);
|
||||
SUnits[i].isAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the entry node first.
|
||||
ScheduleNodeTopDown(Entry, CurCycle);
|
||||
|
||||
// While Available queue is not empty, grab the node with the highest
|
||||
// priority. If it is not ready put it back. Schedule the node.
|
||||
std::vector<SUnit*> NotReady;
|
||||
SUnit *CurNode = NULL;
|
||||
while (!AvailableQueue->empty()) {
|
||||
SUnit *CurNode = AvailableQueue->pop();
|
||||
while (!isReady(CurNode, CurCycle)) {
|
||||
NotReady.push_back(CurNode);
|
||||
CurNode = AvailableQueue->pop();
|
||||
}
|
||||
|
||||
// Add the nodes that aren't ready back onto the available list.
|
||||
AvailableQueue->push_all(NotReady);
|
||||
NotReady.clear();
|
||||
|
||||
ScheduleNodeTopDown(CurNode, CurCycle);
|
||||
}
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Verify that all SUnits were scheduled.
|
||||
bool AnyNotSched = false;
|
||||
for (unsigned i = 0, e = SUnits.size(); i != e; ++i) {
|
||||
if (!SUnits[i].isScheduled) {
|
||||
if (!AnyNotSched)
|
||||
std::cerr << "*** List scheduling failed! ***\n";
|
||||
SUnits[i].dump(&DAG);
|
||||
std::cerr << "has not been scheduled!\n";
|
||||
AnyNotSched = true;
|
||||
}
|
||||
}
|
||||
assert(!AnyNotSched);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// RegReductionPriorityQueue Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a SchedulingPriorityQueue that schedules using Sethi Ullman numbers
|
||||
// to reduce register pressure.
|
||||
//
|
||||
namespace {
|
||||
template<class SF>
|
||||
class RegReductionPriorityQueue;
|
||||
|
||||
/// Sorting functions for the Available queue.
|
||||
struct bu_ls_rr_sort : public std::binary_function<SUnit*, SUnit*, bool> {
|
||||
RegReductionPriorityQueue<bu_ls_rr_sort> *SPQ;
|
||||
bu_ls_rr_sort(RegReductionPriorityQueue<bu_ls_rr_sort> *spq) : SPQ(spq) {}
|
||||
bu_ls_rr_sort(const bu_ls_rr_sort &RHS) : SPQ(RHS.SPQ) {}
|
||||
|
||||
bool operator()(const SUnit* left, const SUnit* right) const;
|
||||
};
|
||||
|
||||
struct td_ls_rr_sort : public std::binary_function<SUnit*, SUnit*, bool> {
|
||||
RegReductionPriorityQueue<td_ls_rr_sort> *SPQ;
|
||||
td_ls_rr_sort(RegReductionPriorityQueue<td_ls_rr_sort> *spq) : SPQ(spq) {}
|
||||
td_ls_rr_sort(const td_ls_rr_sort &RHS) : SPQ(RHS.SPQ) {}
|
||||
|
||||
bool operator()(const SUnit* left, const SUnit* right) const;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace {
|
||||
template<class SF>
|
||||
class RegReductionPriorityQueue : public SchedulingPriorityQueue {
|
||||
std::priority_queue<SUnit*, std::vector<SUnit*>, SF> Queue;
|
||||
|
||||
public:
|
||||
RegReductionPriorityQueue() :
|
||||
Queue(SF(this)) {}
|
||||
|
||||
virtual void initNodes(const std::vector<SUnit> &sunits) {}
|
||||
virtual void releaseState() {}
|
||||
|
||||
virtual int getSethiUllmanNumber(unsigned NodeNum) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool empty() const { return Queue.empty(); }
|
||||
|
||||
void push(SUnit *U) {
|
||||
Queue.push(U);
|
||||
}
|
||||
void push_all(const std::vector<SUnit *> &Nodes) {
|
||||
for (unsigned i = 0, e = Nodes.size(); i != e; ++i)
|
||||
Queue.push(Nodes[i]);
|
||||
}
|
||||
|
||||
SUnit *pop() {
|
||||
SUnit *V = Queue.top();
|
||||
Queue.pop();
|
||||
return V;
|
||||
}
|
||||
};
|
||||
|
||||
template<class SF>
|
||||
class BURegReductionPriorityQueue : public RegReductionPriorityQueue<SF> {
|
||||
// SUnits - The SUnits for the current graph.
|
||||
const std::vector<SUnit> *SUnits;
|
||||
|
||||
// SethiUllmanNumbers - The SethiUllman number for each node.
|
||||
std::vector<int> SethiUllmanNumbers;
|
||||
|
||||
public:
|
||||
BURegReductionPriorityQueue() {}
|
||||
|
||||
void initNodes(const std::vector<SUnit> &sunits) {
|
||||
SUnits = &sunits;
|
||||
// Add pseudo dependency edges for two-address nodes.
|
||||
if (SchedLowerDefNUse)
|
||||
AddPseudoTwoAddrDeps();
|
||||
// Calculate node priorities.
|
||||
CalculatePriorities();
|
||||
}
|
||||
|
||||
void releaseState() {
|
||||
SUnits = 0;
|
||||
SethiUllmanNumbers.clear();
|
||||
}
|
||||
|
||||
int getSethiUllmanNumber(unsigned NodeNum) const {
|
||||
assert(NodeNum < SethiUllmanNumbers.size());
|
||||
return SethiUllmanNumbers[NodeNum];
|
||||
}
|
||||
|
||||
private:
|
||||
void AddPseudoTwoAddrDeps();
|
||||
void CalculatePriorities();
|
||||
int CalcNodePriority(const SUnit *SU);
|
||||
};
|
||||
|
||||
|
||||
template<class SF>
|
||||
class TDRegReductionPriorityQueue : public RegReductionPriorityQueue<SF> {
|
||||
// SUnits - The SUnits for the current graph.
|
||||
const std::vector<SUnit> *SUnits;
|
||||
|
||||
// SethiUllmanNumbers - The SethiUllman number for each node.
|
||||
std::vector<int> SethiUllmanNumbers;
|
||||
|
||||
public:
|
||||
TDRegReductionPriorityQueue() {}
|
||||
|
||||
void initNodes(const std::vector<SUnit> &sunits) {
|
||||
SUnits = &sunits;
|
||||
// Calculate node priorities.
|
||||
CalculatePriorities();
|
||||
}
|
||||
|
||||
void releaseState() {
|
||||
SUnits = 0;
|
||||
SethiUllmanNumbers.clear();
|
||||
}
|
||||
|
||||
int getSethiUllmanNumber(unsigned NodeNum) const {
|
||||
assert(NodeNum < SethiUllmanNumbers.size());
|
||||
return SethiUllmanNumbers[NodeNum];
|
||||
}
|
||||
|
||||
private:
|
||||
void CalculatePriorities();
|
||||
int CalcNodePriority(const SUnit *SU);
|
||||
};
|
||||
}
|
||||
|
||||
// Bottom up
|
||||
bool bu_ls_rr_sort::operator()(const SUnit *left, const SUnit *right) const {
|
||||
unsigned LeftNum = left->NodeNum;
|
||||
unsigned RightNum = right->NodeNum;
|
||||
bool LIsTarget = left->Node->isTargetOpcode();
|
||||
bool RIsTarget = right->Node->isTargetOpcode();
|
||||
int LPriority = SPQ->getSethiUllmanNumber(LeftNum);
|
||||
int RPriority = SPQ->getSethiUllmanNumber(RightNum);
|
||||
bool LIsFloater = LIsTarget && (LPriority == 1 || LPriority == 0);
|
||||
bool RIsFloater = RIsTarget && (RPriority == 1 || RPriority == 0);
|
||||
int LBonus = 0;
|
||||
int RBonus = 0;
|
||||
|
||||
// Schedule floaters (e.g. load from some constant address) and those nodes
|
||||
// with a single predecessor each first. They maintain / reduce register
|
||||
// pressure.
|
||||
if (LIsFloater)
|
||||
LBonus += 2;
|
||||
if (RIsFloater)
|
||||
RBonus += 2;
|
||||
|
||||
if (!SchedLowerDefNUse) {
|
||||
// Special tie breaker: if two nodes share a operand, the one that use it
|
||||
// as a def&use operand is preferred.
|
||||
if (LIsTarget && RIsTarget) {
|
||||
if (left->isTwoAddress && !right->isTwoAddress) {
|
||||
SDNode *DUNode = left->Node->getOperand(0).Val;
|
||||
if (DUNode->isOperand(right->Node))
|
||||
LBonus += 2;
|
||||
}
|
||||
if (!left->isTwoAddress && right->isTwoAddress) {
|
||||
SDNode *DUNode = right->Node->getOperand(0).Val;
|
||||
if (DUNode->isOperand(left->Node))
|
||||
RBonus += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LPriority+LBonus < RPriority+RBonus)
|
||||
return true;
|
||||
else if (LPriority+LBonus == RPriority+RBonus)
|
||||
if (left->NumPredsLeft > right->NumPredsLeft)
|
||||
return true;
|
||||
else if (left->NumPredsLeft+LBonus == right->NumPredsLeft+RBonus)
|
||||
if (left->CycleBound > right->CycleBound)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool isCopyFromLiveIn(const SUnit *SU) {
|
||||
SDNode *N = SU->Node;
|
||||
return N->getOpcode() == ISD::CopyFromReg &&
|
||||
N->getOperand(N->getNumOperands()-1).getValueType() != MVT::Flag;
|
||||
}
|
||||
|
||||
// FIXME: This is probably too slow!
|
||||
static void isReachable(SUnit *SU, SUnit *TargetSU,
|
||||
std::set<SUnit *> &Visited, bool &Reached) {
|
||||
if (Reached) return;
|
||||
if (SU == TargetSU) {
|
||||
Reached = true;
|
||||
return;
|
||||
}
|
||||
if (!Visited.insert(SU).second) return;
|
||||
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator I = SU->Preds.begin(),
|
||||
E = SU->Preds.end(); I != E; ++I)
|
||||
isReachable(I->first, TargetSU, Visited, Reached);
|
||||
}
|
||||
|
||||
static bool isReachable(SUnit *SU, SUnit *TargetSU) {
|
||||
std::set<SUnit *> Visited;
|
||||
bool Reached = false;
|
||||
isReachable(SU, TargetSU, Visited, Reached);
|
||||
return Reached;
|
||||
}
|
||||
|
||||
static SUnit *getDefUsePredecessor(SUnit *SU) {
|
||||
SDNode *DU = SU->Node->getOperand(0).Val;
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator
|
||||
I = SU->Preds.begin(), E = SU->Preds.end(); I != E; ++I) {
|
||||
if (I->second) continue; // ignore chain preds
|
||||
SUnit *PredSU = I->first;
|
||||
if (PredSU->Node == DU)
|
||||
return PredSU;
|
||||
}
|
||||
|
||||
// Must be flagged.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool canClobber(SUnit *SU, SUnit *Op) {
|
||||
if (SU->isTwoAddress)
|
||||
return Op == getDefUsePredecessor(SU);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// AddPseudoTwoAddrDeps - If two nodes share an operand and one of them uses
|
||||
/// it as a def&use operand. Add a pseudo control edge from it to the other
|
||||
/// node (if it won't create a cycle) so the two-address one will be scheduled
|
||||
/// first (lower in the schedule).
|
||||
template<class SF>
|
||||
void BURegReductionPriorityQueue<SF>::AddPseudoTwoAddrDeps() {
|
||||
for (unsigned i = 0, e = SUnits->size(); i != e; ++i) {
|
||||
SUnit *SU = (SUnit *)&((*SUnits)[i]);
|
||||
SDNode *Node = SU->Node;
|
||||
if (!Node->isTargetOpcode())
|
||||
continue;
|
||||
|
||||
if (SU->isTwoAddress) {
|
||||
unsigned Depth = SU->Node->getNodeDepth();
|
||||
SUnit *DUSU = getDefUsePredecessor(SU);
|
||||
if (!DUSU) continue;
|
||||
|
||||
for (std::set<std::pair<SUnit*, bool> >::iterator I = DUSU->Succs.begin(),
|
||||
E = DUSU->Succs.end(); I != E; ++I) {
|
||||
SUnit *SuccSU = I->first;
|
||||
if (SuccSU != SU && !canClobber(SuccSU, DUSU)) {
|
||||
if (SuccSU->Node->getNodeDepth() <= Depth+2 &&
|
||||
!isReachable(SuccSU, SU)) {
|
||||
DEBUG(std::cerr << "Adding an edge from SU # " << SU->NodeNum
|
||||
<< " to SU #" << SuccSU->NodeNum << "\n");
|
||||
if (SU->Preds.insert(std::make_pair(SuccSU, true)).second)
|
||||
SU->NumChainPredsLeft++;
|
||||
if (SuccSU->Succs.insert(std::make_pair(SU, true)).second)
|
||||
SuccSU->NumChainSuccsLeft++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CalcNodePriority - Priority is the Sethi Ullman number.
|
||||
/// Smaller number is the higher priority.
|
||||
template<class SF>
|
||||
int BURegReductionPriorityQueue<SF>::CalcNodePriority(const SUnit *SU) {
|
||||
int &SethiUllmanNumber = SethiUllmanNumbers[SU->NodeNum];
|
||||
if (SethiUllmanNumber != 0)
|
||||
return SethiUllmanNumber;
|
||||
|
||||
unsigned Opc = SU->Node->getOpcode();
|
||||
if (Opc == ISD::TokenFactor || Opc == ISD::CopyToReg)
|
||||
SethiUllmanNumber = INT_MAX - 10;
|
||||
else if (SU->NumSuccsLeft == 0)
|
||||
// If SU does not have a use, i.e. it doesn't produce a value that would
|
||||
// be consumed (e.g. store), then it terminates a chain of computation.
|
||||
// Give it a small SethiUllman number so it will be scheduled right before its
|
||||
// predecessors that it doesn't lengthen their live ranges.
|
||||
SethiUllmanNumber = INT_MIN + 10;
|
||||
else if (SU->NumPredsLeft == 0 &&
|
||||
(Opc != ISD::CopyFromReg || isCopyFromLiveIn(SU)))
|
||||
SethiUllmanNumber = 1;
|
||||
else {
|
||||
int Extra = 0;
|
||||
for (std::set<std::pair<SUnit*, bool> >::const_iterator
|
||||
I = SU->Preds.begin(), E = SU->Preds.end(); I != E; ++I) {
|
||||
if (I->second) continue; // ignore chain preds
|
||||
SUnit *PredSU = I->first;
|
||||
int PredSethiUllman = CalcNodePriority(PredSU);
|
||||
if (PredSethiUllman > SethiUllmanNumber) {
|
||||
SethiUllmanNumber = PredSethiUllman;
|
||||
Extra = 0;
|
||||
} else if (PredSethiUllman == SethiUllmanNumber && !I->second)
|
||||
Extra++;
|
||||
}
|
||||
|
||||
SethiUllmanNumber += Extra;
|
||||
}
|
||||
|
||||
return SethiUllmanNumber;
|
||||
}
|
||||
|
||||
/// CalculatePriorities - Calculate priorities of all scheduling units.
|
||||
template<class SF>
|
||||
void BURegReductionPriorityQueue<SF>::CalculatePriorities() {
|
||||
SethiUllmanNumbers.assign(SUnits->size(), 0);
|
||||
|
||||
for (unsigned i = 0, e = SUnits->size(); i != e; ++i)
|
||||
CalcNodePriority(&(*SUnits)[i]);
|
||||
}
|
||||
|
||||
static unsigned SumOfUnscheduledPredsOfSuccs(const SUnit *SU) {
|
||||
unsigned Sum = 0;
|
||||
for (std::set<std::pair<SUnit*, bool> >::const_iterator
|
||||
I = SU->Succs.begin(), E = SU->Succs.end(); I != E; ++I) {
|
||||
SUnit *SuccSU = I->first;
|
||||
for (std::set<std::pair<SUnit*, bool> >::const_iterator
|
||||
II = SuccSU->Preds.begin(), EE = SuccSU->Preds.end(); II != EE; ++II) {
|
||||
SUnit *PredSU = II->first;
|
||||
if (!PredSU->isScheduled)
|
||||
Sum++;
|
||||
}
|
||||
}
|
||||
|
||||
return Sum;
|
||||
}
|
||||
|
||||
|
||||
// Top down
|
||||
bool td_ls_rr_sort::operator()(const SUnit *left, const SUnit *right) const {
|
||||
unsigned LeftNum = left->NodeNum;
|
||||
unsigned RightNum = right->NodeNum;
|
||||
int LPriority = SPQ->getSethiUllmanNumber(LeftNum);
|
||||
int RPriority = SPQ->getSethiUllmanNumber(RightNum);
|
||||
bool LIsTarget = left->Node->isTargetOpcode();
|
||||
bool RIsTarget = right->Node->isTargetOpcode();
|
||||
bool LIsFloater = LIsTarget && left->NumPreds == 0;
|
||||
bool RIsFloater = RIsTarget && right->NumPreds == 0;
|
||||
unsigned LBonus = (SumOfUnscheduledPredsOfSuccs(left) == 1) ? 2 : 0;
|
||||
unsigned RBonus = (SumOfUnscheduledPredsOfSuccs(right) == 1) ? 2 : 0;
|
||||
|
||||
if (left->NumSuccs == 0 && right->NumSuccs != 0)
|
||||
return false;
|
||||
else if (left->NumSuccs != 0 && right->NumSuccs == 0)
|
||||
return true;
|
||||
|
||||
// Special tie breaker: if two nodes share a operand, the one that use it
|
||||
// as a def&use operand is preferred.
|
||||
if (LIsTarget && RIsTarget) {
|
||||
if (left->isTwoAddress && !right->isTwoAddress) {
|
||||
SDNode *DUNode = left->Node->getOperand(0).Val;
|
||||
if (DUNode->isOperand(right->Node))
|
||||
RBonus += 2;
|
||||
}
|
||||
if (!left->isTwoAddress && right->isTwoAddress) {
|
||||
SDNode *DUNode = right->Node->getOperand(0).Val;
|
||||
if (DUNode->isOperand(left->Node))
|
||||
LBonus += 2;
|
||||
}
|
||||
}
|
||||
if (LIsFloater)
|
||||
LBonus -= 2;
|
||||
if (RIsFloater)
|
||||
RBonus -= 2;
|
||||
if (left->NumSuccs == 1)
|
||||
LBonus += 2;
|
||||
if (right->NumSuccs == 1)
|
||||
RBonus += 2;
|
||||
|
||||
if (LPriority+LBonus < RPriority+RBonus)
|
||||
return true;
|
||||
else if (LPriority == RPriority)
|
||||
if (left->Depth < right->Depth)
|
||||
return true;
|
||||
else if (left->Depth == right->Depth)
|
||||
if (left->NumSuccsLeft > right->NumSuccsLeft)
|
||||
return true;
|
||||
else if (left->NumSuccsLeft == right->NumSuccsLeft)
|
||||
if (left->CycleBound > right->CycleBound)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// CalcNodePriority - Priority is the Sethi Ullman number.
|
||||
/// Smaller number is the higher priority.
|
||||
template<class SF>
|
||||
int TDRegReductionPriorityQueue<SF>::CalcNodePriority(const SUnit *SU) {
|
||||
int &SethiUllmanNumber = SethiUllmanNumbers[SU->NodeNum];
|
||||
if (SethiUllmanNumber != 0)
|
||||
return SethiUllmanNumber;
|
||||
|
||||
unsigned Opc = SU->Node->getOpcode();
|
||||
if (Opc == ISD::TokenFactor || Opc == ISD::CopyToReg)
|
||||
SethiUllmanNumber = INT_MAX - 10;
|
||||
else if (SU->NumSuccsLeft == 0)
|
||||
// If SU does not have a use, i.e. it doesn't produce a value that would
|
||||
// be consumed (e.g. store), then it terminates a chain of computation.
|
||||
// Give it a small SethiUllman number so it will be scheduled right before its
|
||||
// predecessors that it doesn't lengthen their live ranges.
|
||||
SethiUllmanNumber = INT_MIN + 10;
|
||||
else if (SU->NumPredsLeft == 0 &&
|
||||
(Opc != ISD::CopyFromReg || isCopyFromLiveIn(SU)))
|
||||
SethiUllmanNumber = 1;
|
||||
else {
|
||||
int Extra = 0;
|
||||
for (std::set<std::pair<SUnit*, bool> >::const_iterator
|
||||
I = SU->Preds.begin(), E = SU->Preds.end(); I != E; ++I) {
|
||||
if (I->second) continue; // ignore chain preds
|
||||
SUnit *PredSU = I->first;
|
||||
int PredSethiUllman = CalcNodePriority(PredSU);
|
||||
if (PredSethiUllman > SethiUllmanNumber) {
|
||||
SethiUllmanNumber = PredSethiUllman;
|
||||
Extra = 0;
|
||||
} else if (PredSethiUllman == SethiUllmanNumber && !I->second)
|
||||
Extra++;
|
||||
}
|
||||
|
||||
SethiUllmanNumber += Extra;
|
||||
}
|
||||
|
||||
return SethiUllmanNumber;
|
||||
}
|
||||
|
||||
/// CalculatePriorities - Calculate priorities of all scheduling units.
|
||||
template<class SF>
|
||||
void TDRegReductionPriorityQueue<SF>::CalculatePriorities() {
|
||||
SethiUllmanNumbers.assign(SUnits->size(), 0);
|
||||
|
||||
for (unsigned i = 0, e = SUnits->size(); i != e; ++i)
|
||||
CalcNodePriority(&(*SUnits)[i]);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Public Constructor Functions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
llvm::ScheduleDAG* llvm::createBURRListDAGScheduler(SelectionDAG &DAG,
|
||||
MachineBasicBlock *BB) {
|
||||
return new ScheduleDAGRRList(DAG, BB, DAG.getTarget(), true,
|
||||
new BURegReductionPriorityQueue<bu_ls_rr_sort>());
|
||||
}
|
||||
|
||||
llvm::ScheduleDAG* llvm::createTDRRListDAGScheduler(SelectionDAG &DAG,
|
||||
MachineBasicBlock *BB) {
|
||||
return new ScheduleDAGRRList(DAG, BB, DAG.getTarget(), false,
|
||||
new TDRegReductionPriorityQueue<td_ls_rr_sort>());
|
||||
}
|
||||
|
@ -58,36 +58,28 @@ ViewSchedDAGs("view-sched-dags", cl::Hidden,
|
||||
static const bool ViewISelDAGs = 0, ViewSchedDAGs = 0;
|
||||
#endif
|
||||
|
||||
// Scheduling heuristics
|
||||
enum SchedHeuristics {
|
||||
defaultScheduling, // Let the target specify its preference.
|
||||
noScheduling, // No scheduling, emit breadth first sequence.
|
||||
simpleScheduling, // Two pass, min. critical path, max. utilization.
|
||||
simpleNoItinScheduling, // Same as above exact using generic latency.
|
||||
listSchedulingBURR, // Bottom up reg reduction list scheduling.
|
||||
listSchedulingTD // Top-down list scheduler.
|
||||
};
|
||||
|
||||
namespace {
|
||||
cl::opt<SchedHeuristics>
|
||||
cl::opt<ScheduleDAG::SchedHeuristics>
|
||||
ISHeuristic(
|
||||
"sched",
|
||||
cl::desc("Choose scheduling style"),
|
||||
cl::init(defaultScheduling),
|
||||
cl::init(ScheduleDAG::defaultScheduling),
|
||||
cl::values(
|
||||
clEnumValN(defaultScheduling, "default",
|
||||
clEnumValN(ScheduleDAG::defaultScheduling, "default",
|
||||
"Target preferred scheduling style"),
|
||||
clEnumValN(noScheduling, "none",
|
||||
clEnumValN(ScheduleDAG::noScheduling, "none",
|
||||
"No scheduling: breadth first sequencing"),
|
||||
clEnumValN(simpleScheduling, "simple",
|
||||
clEnumValN(ScheduleDAG::simpleScheduling, "simple",
|
||||
"Simple two pass scheduling: minimize critical path "
|
||||
"and maximize processor utilization"),
|
||||
clEnumValN(simpleNoItinScheduling, "simple-noitin",
|
||||
clEnumValN(ScheduleDAG::simpleNoItinScheduling, "simple-noitin",
|
||||
"Simple two pass scheduling: Same as simple "
|
||||
"except using generic latency"),
|
||||
clEnumValN(listSchedulingBURR, "list-burr",
|
||||
"Bottom up register reduction list scheduling"),
|
||||
clEnumValN(listSchedulingTD, "list-td",
|
||||
clEnumValN(ScheduleDAG::listSchedulingBURR, "list-burr",
|
||||
"Bottom-up register reduction list scheduling"),
|
||||
clEnumValN(ScheduleDAG::listSchedulingTDRR, "list-tdrr",
|
||||
"Top-down register reduction list scheduling"),
|
||||
clEnumValN(ScheduleDAG::listSchedulingTD, "list-td",
|
||||
"Top-down list scheduler"),
|
||||
clEnumValEnd));
|
||||
} // namespace
|
||||
@ -3418,7 +3410,7 @@ void SelectionDAGISel::ScheduleAndEmitDAG(SelectionDAG &DAG) {
|
||||
|
||||
switch (ISHeuristic) {
|
||||
default: assert(0 && "Unrecognized scheduling heuristic");
|
||||
case defaultScheduling:
|
||||
case ScheduleDAG::defaultScheduling:
|
||||
if (TLI.getSchedulingPreference() == TargetLowering::SchedulingForLatency)
|
||||
SL = createTDListDAGScheduler(DAG, BB, CreateTargetHazardRecognizer());
|
||||
else {
|
||||
@ -3427,19 +3419,22 @@ void SelectionDAGISel::ScheduleAndEmitDAG(SelectionDAG &DAG) {
|
||||
SL = createBURRListDAGScheduler(DAG, BB);
|
||||
}
|
||||
break;
|
||||
case noScheduling:
|
||||
case ScheduleDAG::noScheduling:
|
||||
SL = createBFS_DAGScheduler(DAG, BB);
|
||||
break;
|
||||
case simpleScheduling:
|
||||
case ScheduleDAG::simpleScheduling:
|
||||
SL = createSimpleDAGScheduler(false, DAG, BB);
|
||||
break;
|
||||
case simpleNoItinScheduling:
|
||||
case ScheduleDAG::simpleNoItinScheduling:
|
||||
SL = createSimpleDAGScheduler(true, DAG, BB);
|
||||
break;
|
||||
case listSchedulingBURR:
|
||||
case ScheduleDAG::listSchedulingBURR:
|
||||
SL = createBURRListDAGScheduler(DAG, BB);
|
||||
break;
|
||||
case listSchedulingTD:
|
||||
case ScheduleDAG::listSchedulingTDRR:
|
||||
SL = createTDRRListDAGScheduler(DAG, BB);
|
||||
break;
|
||||
case ScheduleDAG::listSchedulingTD:
|
||||
SL = createTDListDAGScheduler(DAG, BB, CreateTargetHazardRecognizer());
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user