261 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
//-- SystemZMachineScheduler.cpp - SystemZ Scheduler Interface -*- C++ -*---==//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// -------------------------- Post RA scheduling ---------------------------- //
 | 
						|
// SystemZPostRASchedStrategy is a scheduling strategy which is plugged into
 | 
						|
// the MachineScheduler. It has a sorted Available set of SUs and a pickNode()
 | 
						|
// implementation that looks to optimize decoder grouping and balance the
 | 
						|
// usage of processor resources. Scheduler states are saved for the end
 | 
						|
// region of each MBB, so that a successor block can learn from it.
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "SystemZMachineScheduler.h"
 | 
						|
#include "llvm/CodeGen/MachineLoopInfo.h"
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
#define DEBUG_TYPE "machine-scheduler"
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
// Print the set of SUs
 | 
						|
void SystemZPostRASchedStrategy::SUSet::
 | 
						|
dump(SystemZHazardRecognizer &HazardRec) const {
 | 
						|
  dbgs() << "{";
 | 
						|
  for (auto &SU : *this) {
 | 
						|
    HazardRec.dumpSU(SU, dbgs());
 | 
						|
    if (SU != *rbegin())
 | 
						|
      dbgs() << ",  ";
 | 
						|
  }
 | 
						|
  dbgs() << "}\n";
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
// Try to find a single predecessor that would be interesting for the
 | 
						|
// scheduler in the top-most region of MBB.
 | 
						|
static MachineBasicBlock *getSingleSchedPred(MachineBasicBlock *MBB,
 | 
						|
                                             const MachineLoop *Loop) {
 | 
						|
  MachineBasicBlock *PredMBB = nullptr;
 | 
						|
  if (MBB->pred_size() == 1)
 | 
						|
    PredMBB = *MBB->pred_begin();
 | 
						|
 | 
						|
  // The loop header has two predecessors, return the latch, but not for a
 | 
						|
  // single block loop.
 | 
						|
  if (MBB->pred_size() == 2 && Loop != nullptr && Loop->getHeader() == MBB) {
 | 
						|
    for (MachineBasicBlock *Pred : MBB->predecessors())
 | 
						|
      if (Loop->contains(Pred))
 | 
						|
        PredMBB = (Pred == MBB ? nullptr : Pred);
 | 
						|
  }
 | 
						|
 | 
						|
  assert ((PredMBB == nullptr || !Loop || Loop->contains(PredMBB))
 | 
						|
          && "Loop MBB should not consider predecessor outside of loop.");
 | 
						|
 | 
						|
  return PredMBB;
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::
 | 
						|
advanceTo(MachineBasicBlock::iterator NextBegin) {
 | 
						|
  MachineBasicBlock::iterator LastEmittedMI = HazardRec->getLastEmittedMI();
 | 
						|
  MachineBasicBlock::iterator I =
 | 
						|
    ((LastEmittedMI != nullptr && LastEmittedMI->getParent() == MBB) ?
 | 
						|
     std::next(LastEmittedMI) : MBB->begin());
 | 
						|
 | 
						|
  for (; I != NextBegin; ++I) {
 | 
						|
    if (I->isPosition() || I->isDebugInstr())
 | 
						|
      continue;
 | 
						|
    HazardRec->emitInstruction(&*I);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::initialize(ScheduleDAGMI *dag) {
 | 
						|
  Available.clear();  // -misched-cutoff.
 | 
						|
  LLVM_DEBUG(HazardRec->dumpState(););
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::enterMBB(MachineBasicBlock *NextMBB) {
 | 
						|
  assert ((SchedStates.find(NextMBB) == SchedStates.end()) &&
 | 
						|
          "Entering MBB twice?");
 | 
						|
  LLVM_DEBUG(dbgs() << "** Entering " << printMBBReference(*NextMBB));
 | 
						|
 | 
						|
  MBB = NextMBB;
 | 
						|
 | 
						|
  /// Create a HazardRec for MBB, save it in SchedStates and set HazardRec to
 | 
						|
  /// point to it.
 | 
						|
  HazardRec = SchedStates[MBB] = new SystemZHazardRecognizer(TII, &SchedModel);
 | 
						|
  LLVM_DEBUG(const MachineLoop *Loop = MLI->getLoopFor(MBB);
 | 
						|
             if (Loop && Loop->getHeader() == MBB) dbgs() << " (Loop header)";
 | 
						|
             dbgs() << ":\n";);
 | 
						|
 | 
						|
  // Try to take over the state from a single predecessor, if it has been
 | 
						|
  // scheduled. If this is not possible, we are done.
 | 
						|
  MachineBasicBlock *SinglePredMBB =
 | 
						|
    getSingleSchedPred(MBB, MLI->getLoopFor(MBB));
 | 
						|
  if (SinglePredMBB == nullptr ||
 | 
						|
      SchedStates.find(SinglePredMBB) == SchedStates.end())
 | 
						|
    return;
 | 
						|
 | 
						|
  LLVM_DEBUG(dbgs() << "** Continued scheduling from "
 | 
						|
                    << printMBBReference(*SinglePredMBB) << "\n";);
 | 
						|
 | 
						|
  HazardRec->copyState(SchedStates[SinglePredMBB]);
 | 
						|
  LLVM_DEBUG(HazardRec->dumpState(););
 | 
						|
 | 
						|
  // Emit incoming terminator(s). Be optimistic and assume that branch
 | 
						|
  // prediction will generally do "the right thing".
 | 
						|
  for (MachineInstr &MI : SinglePredMBB->terminators()) {
 | 
						|
    LLVM_DEBUG(dbgs() << "** Emitting incoming branch: "; MI.dump(););
 | 
						|
    bool TakenBranch = (MI.isBranch() &&
 | 
						|
                        (TII->getBranchInfo(MI).isIndirect() ||
 | 
						|
                         TII->getBranchInfo(MI).getMBBTarget() == MBB));
 | 
						|
    HazardRec->emitInstruction(&MI, TakenBranch);
 | 
						|
    if (TakenBranch)
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::leaveMBB() {
 | 
						|
  LLVM_DEBUG(dbgs() << "** Leaving " << printMBBReference(*MBB) << "\n";);
 | 
						|
 | 
						|
  // Advance to first terminator. The successor block will handle terminators
 | 
						|
  // dependent on CFG layout (T/NT branch etc).
 | 
						|
  advanceTo(MBB->getFirstTerminator());
 | 
						|
}
 | 
						|
 | 
						|
SystemZPostRASchedStrategy::
 | 
						|
SystemZPostRASchedStrategy(const MachineSchedContext *C)
 | 
						|
  : MLI(C->MLI),
 | 
						|
    TII(static_cast<const SystemZInstrInfo *>
 | 
						|
        (C->MF->getSubtarget().getInstrInfo())),
 | 
						|
    MBB(nullptr), HazardRec(nullptr) {
 | 
						|
  const TargetSubtargetInfo *ST = &C->MF->getSubtarget();
 | 
						|
  SchedModel.init(ST);
 | 
						|
}
 | 
						|
 | 
						|
SystemZPostRASchedStrategy::~SystemZPostRASchedStrategy() {
 | 
						|
  // Delete hazard recognizers kept around for each MBB.
 | 
						|
  for (auto I : SchedStates) {
 | 
						|
    SystemZHazardRecognizer *hazrec = I.second;
 | 
						|
    delete hazrec;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::initPolicy(MachineBasicBlock::iterator Begin,
 | 
						|
                                            MachineBasicBlock::iterator End,
 | 
						|
                                            unsigned NumRegionInstrs) {
 | 
						|
  // Don't emit the terminators.
 | 
						|
  if (Begin->isTerminator())
 | 
						|
    return;
 | 
						|
 | 
						|
  // Emit any instructions before start of region.
 | 
						|
  advanceTo(Begin);
 | 
						|
}
 | 
						|
 | 
						|
// Pick the next node to schedule.
 | 
						|
SUnit *SystemZPostRASchedStrategy::pickNode(bool &IsTopNode) {
 | 
						|
  // Only scheduling top-down.
 | 
						|
  IsTopNode = true;
 | 
						|
 | 
						|
  if (Available.empty())
 | 
						|
    return nullptr;
 | 
						|
 | 
						|
  // If only one choice, return it.
 | 
						|
  if (Available.size() == 1) {
 | 
						|
    LLVM_DEBUG(dbgs() << "** Only one: ";
 | 
						|
               HazardRec->dumpSU(*Available.begin(), dbgs()); dbgs() << "\n";);
 | 
						|
    return *Available.begin();
 | 
						|
  }
 | 
						|
 | 
						|
  // All nodes that are possible to schedule are stored in the Available set.
 | 
						|
  LLVM_DEBUG(dbgs() << "** Available: "; Available.dump(*HazardRec););
 | 
						|
 | 
						|
  Candidate Best;
 | 
						|
  for (auto *SU : Available) {
 | 
						|
 | 
						|
    // SU is the next candidate to be compared against current Best.
 | 
						|
    Candidate c(SU, *HazardRec);
 | 
						|
 | 
						|
    // Remeber which SU is the best candidate.
 | 
						|
    if (Best.SU == nullptr || c < Best) {
 | 
						|
      Best = c;
 | 
						|
      LLVM_DEBUG(dbgs() << "** Best so far: ";);
 | 
						|
    } else
 | 
						|
      LLVM_DEBUG(dbgs() << "** Tried      : ";);
 | 
						|
    LLVM_DEBUG(HazardRec->dumpSU(c.SU, dbgs()); c.dumpCosts();
 | 
						|
               dbgs() << " Height:" << c.SU->getHeight(); dbgs() << "\n";);
 | 
						|
 | 
						|
    // Once we know we have seen all SUs that affect grouping or use unbuffered
 | 
						|
    // resources, we can stop iterating if Best looks good.
 | 
						|
    if (!SU->isScheduleHigh && Best.noCost())
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  assert (Best.SU != nullptr);
 | 
						|
  return Best.SU;
 | 
						|
}
 | 
						|
 | 
						|
SystemZPostRASchedStrategy::Candidate::
 | 
						|
Candidate(SUnit *SU_, SystemZHazardRecognizer &HazardRec) : Candidate() {
 | 
						|
  SU = SU_;
 | 
						|
 | 
						|
  // Check the grouping cost. For a node that must begin / end a
 | 
						|
  // group, it is positive if it would do so prematurely, or negative
 | 
						|
  // if it would fit naturally into the schedule.
 | 
						|
  GroupingCost = HazardRec.groupingCost(SU);
 | 
						|
 | 
						|
  // Check the resources cost for this SU.
 | 
						|
  ResourcesCost = HazardRec.resourcesCost(SU);
 | 
						|
}
 | 
						|
 | 
						|
bool SystemZPostRASchedStrategy::Candidate::
 | 
						|
operator<(const Candidate &other) {
 | 
						|
 | 
						|
  // Check decoder grouping.
 | 
						|
  if (GroupingCost < other.GroupingCost)
 | 
						|
    return true;
 | 
						|
  if (GroupingCost > other.GroupingCost)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // Compare the use of resources.
 | 
						|
  if (ResourcesCost < other.ResourcesCost)
 | 
						|
    return true;
 | 
						|
  if (ResourcesCost > other.ResourcesCost)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // Higher SU is otherwise generally better.
 | 
						|
  if (SU->getHeight() > other.SU->getHeight())
 | 
						|
    return true;
 | 
						|
  if (SU->getHeight() < other.SU->getHeight())
 | 
						|
    return false;
 | 
						|
 | 
						|
  // If all same, fall back to original order.
 | 
						|
  if (SU->NodeNum < other.SU->NodeNum)
 | 
						|
    return true;
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::schedNode(SUnit *SU, bool IsTopNode) {
 | 
						|
  LLVM_DEBUG(dbgs() << "** Scheduling SU(" << SU->NodeNum << ") ";
 | 
						|
             if (Available.size() == 1) dbgs() << "(only one) ";
 | 
						|
             Candidate c(SU, *HazardRec); c.dumpCosts(); dbgs() << "\n";);
 | 
						|
 | 
						|
  // Remove SU from Available set and update HazardRec.
 | 
						|
  Available.erase(SU);
 | 
						|
  HazardRec->EmitInstruction(SU);
 | 
						|
}
 | 
						|
 | 
						|
void SystemZPostRASchedStrategy::releaseTopNode(SUnit *SU) {
 | 
						|
  // Set isScheduleHigh flag on all SUs that we want to consider first in
 | 
						|
  // pickNode().
 | 
						|
  const MCSchedClassDesc *SC = HazardRec->getSchedClass(SU);
 | 
						|
  bool AffectsGrouping = (SC->isValid() && (SC->BeginGroup || SC->EndGroup));
 | 
						|
  SU->isScheduleHigh = (AffectsGrouping || SU->isUnbuffered);
 | 
						|
 | 
						|
  // Put all released SUs in the Available set.
 | 
						|
  Available.insert(SU);
 | 
						|
}
 |