[llvm] r176498 - R600: initial scheduler code

Andrew Trick atrick at apple.com
Tue Mar 5 15:59:31 PST 2013


On Mar 5, 2013, at 3:12 PM, Vincent Lejeune <vljn at ovi.com> wrote:

> Hi Andy,
> 
> Thank for your review.
> I'd like to put some feature into this scheduler before using some unit tests.
> Currently ScheduleDAGInstr enforces dependencies between instructions writing/reading to a reg although not to the same subreg.
> Our arch does not have constrains on accessing different subreg of a 128 bits regs thus schedule pass has to remove such deps.
> In a previous patch this was done in the initialize function but I think this would be better if it was directly supported by ScheduleDAGInstr using a target hook.

Yes. I was deferring this partially because of the problems you're now seeing in handleMove. I was waiting until LiveIntervals understood subregisters, which would result in a cleaner, more robust implementation.

> I'm working on this atm, currently I'm experimenting things in R600MachineRegister.cpp following ScheduleDAGInstr algorithm :
> http://cgit.freedesktop.org/~vlj/llvm/commit/?h=codesize4&id=62a4109bd126efc1f267843402b624b7729c79f7
> This is WIP, I'm evaluating different idea (using a VReg2SUnit structure that would store several SUnit*, one for each accessed subreg, or
> having a SubVReg2SUnit structure that is similar to the current VReg2SUnit but also store subreg)
> 
> Changing ScheduleDAG also means changing schedule pass behavior and I don't know how to properly catch this in a test case ; I have (few) tests but
> they check the expected result of the complete schedule strategy (ie without deps between subreg).

You should do this in postprocessDAG. You can use ScheduleDAGMI::addMutation() without even defining your own ScheduleDAG subclass. Note that findRootsAndBiasEdges is supposed to be called before SchedImpl->initialized, so that edges are biased before potentially running some DAG analysis for some targets.

This approach is a good one for now. It will only affect your target. It's just more expensive in terms of compile time than doing it during initial DAG building. Hopefully that isn't a serious problem. Eventually the shared DAG builder will support this in a clean way.

-Andy

> ----- Mail original -----
>> De : Andrew Trick <atrick at apple.com>
>> À : Vincent Lejeune <vljn at ovi.com>
>> Cc : llvm-commits at cs.uiuc.edu
>> Envoyé le : Mardi 5 mars 2013 20h29
>> Objet : Re: [llvm] r176498 - R600: initial scheduler code
>> 
>> 
>> On Mar 5, 2013, at 10:41 AM, Vincent Lejeune <vljn at ovi.com> wrote:
>> 
>>> Author: vljn
>>> Date: Tue Mar  5 12:41:32 2013
>>> New Revision: 176498
>>> 
>>> URL: http://llvm.org/viewvc/llvm-project?rev=176498&view=rev
>>> Log:
>>> R600: initial scheduler code
>>> 
>>> This is a skeleton for a pre-RA MachineInstr scheduler strategy. Currently
>>> it only tries to expose more parallelism for ALU instructions (this also
>>> makes the distribution of GPR channels more uniform and increases the
>>> chances of ALU instructions to be packed together in a single VLIW group).
>>> Also it tries to reduce clause switching by grouping instruction of the
>>> same kind (ALU/FETCH/CF) together.
>>> 
>>> Vincent Lejeune:
>>> - Support for VLIW4 Slot assignement
>>> - Recomputation of ScheduleDAG to get more parallelism opportunities
>>> 
>>> Tom Stellard:
>>> - Fix assertion failure when trying to determine an instruction's slot
>>>    based on its destination register's class
>>> - Fix some compiler warnings
>>> 
>>> Vincent Lejeune: [v2]
>>> - Remove recomputation of ScheduleDAG (will be provided in a later patch)
>>> - Improve estimation of an ALU clause size so that heuristic does not emit 
>> cf
>>> instructions at the wrong position.
>>> - Make schedule heuristic smarter using SUnit Depth
>>> - Take constant read limitations into account
>>> 
>>> Vincent Lejeune: [v3]
>>> - Fix some uninitialized values in ConstPair
>>> - Add asserts to ensure an ALU slot is always populated
>>> 
>>> Added:
>>>     llvm/trunk/lib/Target/R600/R600MachineScheduler.cpp
>>>     llvm/trunk/lib/Target/R600/R600MachineScheduler.h
>>> Modified:
>>>     llvm/trunk/lib/Target/R600/AMDGPUTargetMachine.cpp
>>> 
>>> Modified: llvm/trunk/lib/Target/R600/AMDGPUTargetMachine.cpp
>>> URL: 
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/R600/AMDGPUTargetMachine.cpp?rev=176498&r1=176497&r2=176498&view=diff
>>> 
>> ==============================================================================
>>> --- llvm/trunk/lib/Target/R600/AMDGPUTargetMachine.cpp (original)
>>> +++ llvm/trunk/lib/Target/R600/AMDGPUTargetMachine.cpp Tue Mar  5 12:41:32 
>> 2013
>>> @@ -17,6 +17,7 @@
>>> #include "AMDGPU.h"
>>> #include "R600ISelLowering.h"
>>> #include "R600InstrInfo.h"
>>> +#include "R600MachineScheduler.h"
>>> #include "SIISelLowering.h"
>>> #include "SIInstrInfo.h"
>>> #include "llvm/Analysis/Passes.h"
>>> @@ -39,6 +40,14 @@ extern "C" void LLVMInitializeR600Target
>>>    RegisterTargetMachine<AMDGPUTargetMachine> X(TheAMDGPUTarget);
>>> }
>>> 
>>> +static ScheduleDAGInstrs *createR600MachineScheduler(MachineSchedContext 
>> *C) {
>>> +  return new ScheduleDAGMI(C, new R600SchedStrategy());
>>> +}
>>> +
>>> +static MachineSchedRegistry
>>> +SchedCustomRegistry("r600", "Run R600's custom 
>> scheduler",
>>> +                    createR600MachineScheduler);
>>> +
>>> AMDGPUTargetMachine::AMDGPUTargetMachine(const Target &T, StringRef TT,
>>>      StringRef CPU, StringRef FS,
>>>    TargetOptions Options,
>>> @@ -70,7 +79,13 @@ namespace {
>>> class AMDGPUPassConfig : public TargetPassConfig {
>>> public:
>>>    AMDGPUPassConfig(AMDGPUTargetMachine *TM, PassManagerBase &PM)
>>> -    : TargetPassConfig(TM, PM) {}
>>> +    : TargetPassConfig(TM, PM) {
>>> +    const AMDGPUSubtarget &ST = 
>> TM->getSubtarget<AMDGPUSubtarget>();
>>> +    if (ST.device()->getGeneration() <= AMDGPUDeviceInfo::HD6XXX) {
>>> +      enablePass(&MachineSchedulerID);
>>> +      MachineSchedRegistry::setDefault(createR600MachineScheduler);
>>> +    }
>>> +  }
>>> 
>>>    AMDGPUTargetMachine &getAMDGPUTargetMachine() const {
>>>      return getTM<AMDGPUTargetMachine>();
>>> 
>>> Added: llvm/trunk/lib/Target/R600/R600MachineScheduler.cpp
>>> URL: 
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/R600/R600MachineScheduler.cpp?rev=176498&view=auto
>>> 
>> ==============================================================================
>>> --- llvm/trunk/lib/Target/R600/R600MachineScheduler.cpp (added)
>>> +++ llvm/trunk/lib/Target/R600/R600MachineScheduler.cpp Tue Mar  5 12:41:32 
>> 2013
>>> @@ -0,0 +1,487 @@
>>> +//===-- R600MachineScheduler.cpp - R600 Scheduler Interface -*- C++ 
>> -*-----===//
>>> +//
>>> +//                     The LLVM Compiler Infrastructure
>>> +//
>>> +// This file is distributed under the University of Illinois Open Source
>>> +// License. See LICENSE.TXT for details.
>>> +//
>>> 
>> +//===----------------------------------------------------------------------===//
>>> +//
>>> +/// \file
>>> +/// \brief R600 Machine Scheduler interface
>>> +// TODO: Scheduling is optimised for VLIW4 arch, modify it to support 
>> TRANS slot
>>> +//
>>> 
>> +//===----------------------------------------------------------------------===//
>>> +
>>> +#define DEBUG_TYPE "misched"
>>> +
>>> +#include "R600MachineScheduler.h"
>>> +#include "llvm/CodeGen/MachineRegisterInfo.h"
>>> +#include "llvm/CodeGen/LiveIntervalAnalysis.h"
>>> +#include "llvm/Pass.h"
>>> +#include "llvm/PassManager.h"
>>> +#include <set>
>>> +#include <iostream>
>>> +using namespace llvm;
>>> +
>>> +void R600SchedStrategy::initialize(ScheduleDAGMI *dag) {
>>> +
>>> +  DAG = dag;
>>> +  TII = static_cast<const R600InstrInfo*>(DAG->TII);
>>> +  TRI = static_cast<const R600RegisterInfo*>(DAG->TRI);
>>> +  MRI = &DAG->MRI;
>>> +  Available[IDAlu]->clear();
>>> +  Available[IDFetch]->clear();
>>> +  Available[IDOther]->clear();
>>> +  CurInstKind = IDOther;
>>> +  CurEmitted = 0;
>>> +  OccupedSlotsMask = 15;
>>> +  memset(InstructionsGroupCandidate, 0, 
>> sizeof(InstructionsGroupCandidate));
>>> +  InstKindLimit[IDAlu] = 120; // 120 minus 8 for security
>>> +
>>> +
>>> +  const AMDGPUSubtarget &ST = 
>> DAG->TM.getSubtarget<AMDGPUSubtarget>();
>>> +  if (ST.device()->getGeneration() <= AMDGPUDeviceInfo::HD5XXX) {
>>> +    InstKindLimit[IDFetch] = 7; // 8 minus 1 for security
>>> +  } else {
>>> +    InstKindLimit[IDFetch] = 15; // 16 minus 1 for security
>>> +  }
>>> +}
>>> +
>>> +void R600SchedStrategy::MoveUnits(ReadyQueue *QSrc, ReadyQueue *QDst)
>>> +{
>>> +  if (QSrc->empty())
>>> +    return;
>>> +  for (ReadyQueue::iterator I = QSrc->begin(),
>>> +      E = QSrc->end(); I != E; ++I) {
>>> +    (*I)->NodeQueueId &= ~QSrc->getID();
>>> +    QDst->push(*I);
>>> +  }
>>> +  QSrc->clear();
>>> +}
>>> +
>>> +SUnit* R600SchedStrategy::pickNode(bool &IsTopNode) {
>>> +  SUnit *SU = 0;
>>> +  IsTopNode = true;
>>> +  NextInstKind = IDOther;
>>> +
>>> +  // check if we might want to switch current clause type
>>> +  bool AllowSwitchToAlu = (CurInstKind == IDOther) ||
>>> +      (CurEmitted > InstKindLimit[CurInstKind]) ||
>>> +      (Available[CurInstKind]->empty());
>>> +  bool AllowSwitchFromAlu = (CurEmitted > InstKindLimit[CurInstKind]) 
>> &&
>>> +      (!Available[IDFetch]->empty() || 
>> !Available[IDOther]->empty());
>>> +
>>> +  if ((AllowSwitchToAlu && CurInstKind != IDAlu) ||
>>> +      (!AllowSwitchFromAlu && CurInstKind == IDAlu)) {
>>> +    // try to pick ALU
>>> +    SU = pickAlu();
>>> +    if (SU) {
>>> +      if (CurEmitted >  InstKindLimit[IDAlu])
>>> +        CurEmitted = 0;
>>> +      NextInstKind = IDAlu;
>>> +    }
>>> +  }
>>> +
>>> +  if (!SU) {
>>> +    // try to pick FETCH
>>> +    SU = pickOther(IDFetch);
>>> +    if (SU)
>>> +      NextInstKind = IDFetch;
>>> +  }
>>> +
>>> +  // try to pick other
>>> +  if (!SU) {
>>> +    SU = pickOther(IDOther);
>>> +    if (SU)
>>> +      NextInstKind = IDOther;
>>> +  }
>>> +
>>> +  DEBUG(
>>> +      if (SU) {
>>> +        dbgs() << "picked node: ";
>>> +        SU->dump(DAG);
>>> +      } else {
>>> +        dbgs() << "NO NODE ";
>>> +        for (int i = 0; i < IDLast; ++i) {
>>> +          Available[i]->dump();
>>> +          Pending[i]->dump();
>>> +        }
>>> +        for (unsigned i = 0; i < DAG->SUnits.size(); i++) {
>>> +          const SUnit &S = DAG->SUnits[i];
>>> +          if (!S.isScheduled)
>>> +            S.dump(DAG);
>>> +        }
>>> +      }
>>> +  );
>>> +
>>> +  return SU;
>>> +}
>>> +
>>> +void R600SchedStrategy::schedNode(SUnit *SU, bool IsTopNode) {
>>> +
>>> +  DEBUG(dbgs() << "scheduled: ");
>>> +  DEBUG(SU->dump(DAG));
>>> +
>>> +  if (NextInstKind != CurInstKind) {
>>> +    DEBUG(dbgs() << "Instruction Type Switch\n");
>>> +    if (NextInstKind != IDAlu)
>>> +      OccupedSlotsMask = 15;
>>> +    CurEmitted = 0;
>>> +    CurInstKind = NextInstKind;
>>> +  }
>>> +
>>> +  if (CurInstKind == IDAlu) {
>>> +    switch (getAluKind(SU)) {
>>> +    case AluT_XYZW:
>>> +      CurEmitted += 4;
>>> +      break;
>>> +    case AluDiscarded:
>>> +      break;
>>> +    default: {
>>> +      ++CurEmitted;
>>> +      for (MachineInstr::mop_iterator It = 
>> SU->getInstr()->operands_begin(),
>>> +          E = SU->getInstr()->operands_end(); It != E; ++It) {
>>> +        MachineOperand &MO = *It;
>>> +        if (MO.isReg() && MO.getReg() == AMDGPU::ALU_LITERAL_X)
>>> +          ++CurEmitted;
>>> +      }
>>> +    }
>>> +    }
>>> +  } else {
>>> +    ++CurEmitted;
>>> +  }
>>> +
>>> +
>>> +  DEBUG(dbgs() << CurEmitted << " Instructions Emitted in 
>> this clause\n");
>>> +
>>> +  if (CurInstKind != IDFetch) {
>>> +    MoveUnits(Pending[IDFetch], Available[IDFetch]);
>>> +  }
>>> +  MoveUnits(Pending[IDOther], Available[IDOther]);
>>> +}
>>> +
>>> +void R600SchedStrategy::releaseTopNode(SUnit *SU) {
>>> +  int IK = getInstKind(SU);
>>> +
>>> +  DEBUG(dbgs() << IK << " <= ");
>>> +  DEBUG(SU->dump(DAG));
>>> +
>>> +  Pending[IK]->push(SU);
>>> +}
>>> +
>>> +void R600SchedStrategy::releaseBottomNode(SUnit *SU) {
>>> +}
>>> +
>>> +bool R600SchedStrategy::regBelongsToClass(unsigned Reg,
>>> +                                          const TargetRegisterClass *RC) 
>> const {
>>> +  if (!TargetRegisterInfo::isVirtualRegister(Reg)) {
>>> +    return RC->contains(Reg);
>>> +  } else {
>>> +    return MRI->getRegClass(Reg) == RC;
>>> +  }
>>> +}
>>> +
>>> +R600SchedStrategy::AluKind R600SchedStrategy::getAluKind(SUnit *SU) const 
>> {
>>> +  MachineInstr *MI = SU->getInstr();
>>> +
>>> +    switch (MI->getOpcode()) {
>>> +    case AMDGPU::INTERP_PAIR_XY:
>>> +    case AMDGPU::INTERP_PAIR_ZW:
>>> +    case AMDGPU::INTERP_VEC_LOAD:
>>> +      return AluT_XYZW;
>>> +    case AMDGPU::COPY:
>>> +      if 
>> (TargetRegisterInfo::isPhysicalRegister(MI->getOperand(1).getReg())) {
>>> +        // %vregX = COPY Tn_X is likely to be discarded in favor of an
>>> +        // assignement of Tn_X to %vregX, don't considers it in 
>> scheduling
>>> +        return AluDiscarded;
>>> +      }
>>> +      else if (MI->getOperand(1).isUndef()) {
>>> +        // MI will become a KILL, don't considers it in scheduling
>>> +        return AluDiscarded;
>>> +      }
>>> +    default:
>>> +      break;
>>> +    }
>>> +
>>> +    // Does the instruction take a whole IG ?
>>> +    if(TII->isVector(*MI) ||
>>> +        TII->isCubeOp(MI->getOpcode()) ||
>>> +        TII->isReductionOp(MI->getOpcode()))
>>> +      return AluT_XYZW;
>>> +
>>> +    // Is the result already assigned to a channel ?
>>> +    unsigned DestSubReg = MI->getOperand(0).getSubReg();
>>> +    switch (DestSubReg) {
>>> +    case AMDGPU::sub0:
>>> +      return AluT_X;
>>> +    case AMDGPU::sub1:
>>> +      return AluT_Y;
>>> +    case AMDGPU::sub2:
>>> +      return AluT_Z;
>>> +    case AMDGPU::sub3:
>>> +      return AluT_W;
>>> +    default:
>>> +      break;
>>> +    }
>>> +
>>> +    // Is the result already member of a X/Y/Z/W class ?
>>> +    unsigned DestReg = MI->getOperand(0).getReg();
>>> +    if (regBelongsToClass(DestReg, &AMDGPU::R600_TReg32_XRegClass) ||
>>> +        regBelongsToClass(DestReg, &AMDGPU::R600_AddrRegClass))
>>> +      return AluT_X;
>>> +    if (regBelongsToClass(DestReg, &AMDGPU::R600_TReg32_YRegClass))
>>> +      return AluT_Y;
>>> +    if (regBelongsToClass(DestReg, &AMDGPU::R600_TReg32_ZRegClass))
>>> +      return AluT_Z;
>>> +    if (regBelongsToClass(DestReg, &AMDGPU::R600_TReg32_WRegClass))
>>> +      return AluT_W;
>>> +    if (regBelongsToClass(DestReg, &AMDGPU::R600_Reg128RegClass))
>>> +      return AluT_XYZW;
>>> +
>>> +    return AluAny;
>>> +
>>> +}
>>> +
>>> +int R600SchedStrategy::getInstKind(SUnit* SU) {
>>> +  int Opcode = SU->getInstr()->getOpcode();
>>> +
>>> +  if (TII->isALUInstr(Opcode)) {
>>> +    return IDAlu;
>>> +  }
>>> +
>>> +  switch (Opcode) {
>>> +  case AMDGPU::COPY:
>>> +  case AMDGPU::CONST_COPY:
>>> +  case AMDGPU::INTERP_PAIR_XY:
>>> +  case AMDGPU::INTERP_PAIR_ZW:
>>> +  case AMDGPU::INTERP_VEC_LOAD:
>>> +  case AMDGPU::DOT4_eg_pseudo:
>>> +  case AMDGPU::DOT4_r600_pseudo:
>>> +    return IDAlu;
>>> +  case AMDGPU::TEX_VTX_CONSTBUF:
>>> +  case AMDGPU::TEX_VTX_TEXBUF:
>>> +  case AMDGPU::TEX_LD:
>>> +  case AMDGPU::TEX_GET_TEXTURE_RESINFO:
>>> +  case AMDGPU::TEX_GET_GRADIENTS_H:
>>> +  case AMDGPU::TEX_GET_GRADIENTS_V:
>>> +  case AMDGPU::TEX_SET_GRADIENTS_H:
>>> +  case AMDGPU::TEX_SET_GRADIENTS_V:
>>> +  case AMDGPU::TEX_SAMPLE:
>>> +  case AMDGPU::TEX_SAMPLE_C:
>>> +  case AMDGPU::TEX_SAMPLE_L:
>>> +  case AMDGPU::TEX_SAMPLE_C_L:
>>> +  case AMDGPU::TEX_SAMPLE_LB:
>>> +  case AMDGPU::TEX_SAMPLE_C_LB:
>>> +  case AMDGPU::TEX_SAMPLE_G:
>>> +  case AMDGPU::TEX_SAMPLE_C_G:
>>> +  case AMDGPU::TXD:
>>> +  case AMDGPU::TXD_SHADOW:
>>> +    return IDFetch;
>>> +  default:
>>> +    DEBUG(
>>> +        dbgs() << "other inst: ";
>>> +        SU->dump(DAG);
>>> +    );
>>> +    return IDOther;
>>> +  }
>>> +}
>>> +
>>> +class ConstPairs {
>>> +private:
>>> +  unsigned XYPair;
>>> +  unsigned ZWPair;
>>> +public:
>>> +  ConstPairs(unsigned ReadConst[3]) : XYPair(0), ZWPair(0) {
>>> +    for (unsigned i = 0; i < 3; i++) {
>>> +      unsigned ReadConstChan = ReadConst[i] & 3;
>>> +      unsigned ReadConstIndex = ReadConst[i] & (~3);
>>> +      if (ReadConstChan < 2) {
>>> +        if (!XYPair) {
>>> +          XYPair = ReadConstIndex;
>>> +        }
>>> +      } else {
>>> +        if (!ZWPair) {
>>> +          ZWPair = ReadConstIndex;
>>> +        }
>>> +      }
>>> +    }
>>> +  }
>>> +
>>> +  bool isCompatibleWith(const ConstPairs& CP) const {
>>> +    return (!XYPair || !CP.XYPair || CP.XYPair == XYPair) &&
>>> +        (!ZWPair || !CP.ZWPair || CP.ZWPair == ZWPair);
>>> +  }
>>> +};
>>> +
>>> +static
>>> +const ConstPairs getPairs(const R600InstrInfo *TII, const 
>> MachineInstr& MI) {
>>> +  unsigned ReadConsts[3] = {0, 0, 0};
>>> +  R600Operands::Ops OpTable[3][2] = {
>>> +    {R600Operands::SRC0, R600Operands::SRC0_SEL},
>>> +    {R600Operands::SRC1, R600Operands::SRC1_SEL},
>>> +    {R600Operands::SRC2, R600Operands::SRC2_SEL},
>>> +  };
>>> +
>>> +  if (!TII->isALUInstr(MI.getOpcode()))
>>> +    return ConstPairs(ReadConsts);
>>> +
>>> +  for (unsigned i = 0; i < 3; i++) {
>>> +    int SrcIdx = TII->getOperandIdx(MI.getOpcode(), OpTable[i][0]);
>>> +    if (SrcIdx < 0)
>>> +      break;
>>> +    if (MI.getOperand(SrcIdx).getReg() == AMDGPU::ALU_CONST)
>>> +      ReadConsts[i] =MI.getOperand(
>>> +          TII->getOperandIdx(MI.getOpcode(), OpTable[i][1])).getImm();
>>> +  }
>>> +  return ConstPairs(ReadConsts);
>>> +}
>>> +
>>> +bool
>>> +R600SchedStrategy::isBundleable(const MachineInstr& MI) {
>>> +  const ConstPairs &MIPair = getPairs(TII, MI);
>>> +  for (unsigned i = 0; i < 4; i++) {
>>> +    if (!InstructionsGroupCandidate[i])
>>> +      continue;
>>> +    const ConstPairs &IGPair = getPairs(TII,
>>> +        *InstructionsGroupCandidate[i]->getInstr());
>>> +    if (!IGPair.isCompatibleWith(MIPair))
>>> +      return false;
>>> +  }
>>> +  return true;
>>> +}
>>> +
>>> +SUnit *R600SchedStrategy::PopInst(std::multiset<SUnit *, 
>> CompareSUnit> &Q) {
>>> +  if (Q.empty())
>>> +    return NULL;
>>> +  for (std::set<SUnit *, CompareSUnit>::iterator It = Q.begin(), E = 
>> Q.end();
>>> +      It != E; ++It) {
>>> +    SUnit *SU = *It;
>>> +    if (isBundleable(*SU->getInstr())) {
>>> +      Q.erase(It);
>>> +      return SU;
>>> +    }
>>> +  }
>>> +  return NULL;
>>> +}
>>> +
>>> +void R600SchedStrategy::LoadAlu() {
>>> +  ReadyQueue *QSrc = Pending[IDAlu];
>>> +  for (ReadyQueue::iterator I = QSrc->begin(),
>>> +        E = QSrc->end(); I != E; ++I) {
>>> +      (*I)->NodeQueueId &= ~QSrc->getID();
>>> +      AluKind AK = getAluKind(*I);
>>> +      AvailableAlus[AK].insert(*I);
>>> +    }
>>> +    QSrc->clear();
>>> +}
>>> +
>>> +void R600SchedStrategy::PrepareNextSlot() {
>>> +  DEBUG(dbgs() << "New Slot\n");
>>> +  assert (OccupedSlotsMask && "Slot wasn't filled");
>>> +  OccupedSlotsMask = 0;
>>> +  memset(InstructionsGroupCandidate, 0, 
>> sizeof(InstructionsGroupCandidate));
>>> +  LoadAlu();
>>> +}
>>> +
>>> +void R600SchedStrategy::AssignSlot(MachineInstr* MI, unsigned Slot) {
>>> +  unsigned DestReg = MI->getOperand(0).getReg();
>>> +  // PressureRegister crashes if an operand is def and used in the same 
>> inst
>>> +  // and we try to constraint its regclass
>>> +  for (MachineInstr::mop_iterator It = MI->operands_begin(),
>>> +      E = MI->operands_end(); It != E; ++It) {
>>> +    MachineOperand &MO = *It;
>>> +    if (MO.isReg() && !MO.isDef() &&
>>> +        MO.getReg() == MI->getOperand(0).getReg())
>>> +      return;
>>> +  }
>>> +  // Constrains the regclass of DestReg to assign it to Slot
>>> +  switch (Slot) {
>>> +  case 0:
>>> +    MRI->constrainRegClass(DestReg, 
>> &AMDGPU::R600_TReg32_XRegClass);
>>> +    break;
>>> +  case 1:
>>> +    MRI->constrainRegClass(DestReg, 
>> &AMDGPU::R600_TReg32_YRegClass);
>>> +    break;
>>> +  case 2:
>>> +    MRI->constrainRegClass(DestReg, 
>> &AMDGPU::R600_TReg32_ZRegClass);
>>> +    break;
>>> +  case 3:
>>> +    MRI->constrainRegClass(DestReg, 
>> &AMDGPU::R600_TReg32_WRegClass);
>>> +    break;
>>> +  }
>>> +}
>>> +
>>> +SUnit *R600SchedStrategy::AttemptFillSlot(unsigned Slot) {
>>> +  static const AluKind IndexToID[] = {AluT_X, AluT_Y, AluT_Z, AluT_W};
>>> +  SUnit *SlotedSU = PopInst(AvailableAlus[IndexToID[Slot]]);
>>> +  SUnit *UnslotedSU = PopInst(AvailableAlus[AluAny]);
>>> +  if (!UnslotedSU) {
>>> +    return SlotedSU;
>>> +  } else if (!SlotedSU) {
>>> +    AssignSlot(UnslotedSU->getInstr(), Slot);
>>> +    return UnslotedSU;
>>> +  } else {
>>> +    //Determine which one to pick (the lesser one)
>>> +    if (CompareSUnit()(SlotedSU, UnslotedSU)) {
>>> +      AvailableAlus[AluAny].insert(UnslotedSU);
>>> +      return SlotedSU;
>>> +    } else {
>>> +      AvailableAlus[IndexToID[Slot]].insert(SlotedSU);
>>> +      AssignSlot(UnslotedSU->getInstr(), Slot);
>>> +      return UnslotedSU;
>>> +    }
>>> +  }
>>> +}
>>> +
>>> +bool R600SchedStrategy::isAvailablesAluEmpty() const {
>>> +  return Pending[IDAlu]->empty() && 
>> AvailableAlus[AluAny].empty() &&
>>> +      AvailableAlus[AluT_XYZW].empty() && 
>> AvailableAlus[AluT_X].empty() &&
>>> +      AvailableAlus[AluT_Y].empty() && 
>> AvailableAlus[AluT_Z].empty() &&
>>> +      AvailableAlus[AluT_W].empty() && 
>> AvailableAlus[AluDiscarded].empty();
>>> +}
>>> +
>>> +SUnit* R600SchedStrategy::pickAlu() {
>>> +  while (!isAvailablesAluEmpty()) {
>>> +    if (!OccupedSlotsMask) {
>>> +      // Flush physical reg copies (RA will discard them)
>>> +      if (!AvailableAlus[AluDiscarded].empty()) {
>>> +        OccupedSlotsMask = 15;
>>> +        return PopInst(AvailableAlus[AluDiscarded]);
>>> +      }
>>> +      // If there is a T_XYZW alu available, use it
>>> +      if (!AvailableAlus[AluT_XYZW].empty()) {
>>> +        OccupedSlotsMask = 15;
>>> +        return PopInst(AvailableAlus[AluT_XYZW]);
>>> +      }
>>> +    }
>>> +    for (unsigned Chan = 0; Chan < 4; ++Chan) {
>>> +      bool isOccupied = OccupedSlotsMask & (1 << Chan);
>>> +      if (!isOccupied) {
>>> +        SUnit *SU = AttemptFillSlot(Chan);
>>> +        if (SU) {
>>> +          OccupedSlotsMask |= (1 << Chan);
>>> +          InstructionsGroupCandidate[Chan] = SU;
>>> +          return SU;
>>> +        }
>>> +      }
>>> +    }
>>> +    PrepareNextSlot();
>>> +  }
>>> +  return NULL;
>>> +}
>>> +
>>> +SUnit* R600SchedStrategy::pickOther(int QID) {
>>> +  SUnit *SU = 0;
>>> +  ReadyQueue *AQ = Available[QID];
>>> +
>>> +  if (AQ->empty()) {
>>> +    MoveUnits(Pending[QID], AQ);
>>> +  }
>>> +  if (!AQ->empty()) {
>>> +    SU = *AQ->begin();
>>> +    AQ->remove(AQ->begin());
>>> +  }
>>> +  return SU;
>>> +}
>>> +
>>> 
>>> Added: llvm/trunk/lib/Target/R600/R600MachineScheduler.h
>>> URL: 
>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/R600/R600MachineScheduler.h?rev=176498&view=auto
>>> 
>> ==============================================================================
>>> --- llvm/trunk/lib/Target/R600/R600MachineScheduler.h (added)
>>> +++ llvm/trunk/lib/Target/R600/R600MachineScheduler.h Tue Mar  5 12:41:32 
>> 2013
>>> @@ -0,0 +1,121 @@
>>> +//===-- R600MachineScheduler.h - R600 Scheduler Interface -*- C++ 
>> -*-------===//
>>> +//
>>> +//                     The LLVM Compiler Infrastructure
>>> +//
>>> +// This file is distributed under the University of Illinois Open Source
>>> +// License. See LICENSE.TXT for details.
>>> +//
>>> 
>> +//===----------------------------------------------------------------------===//
>>> +//
>>> +/// \file
>>> +/// \brief R600 Machine Scheduler interface
>>> +//
>>> 
>> +//===----------------------------------------------------------------------===//
>>> +
>>> +#ifndef R600MACHINESCHEDULER_H_
>>> +#define R600MACHINESCHEDULER_H_
>>> +
>>> +#include "R600InstrInfo.h"
>>> +#include "llvm/CodeGen/MachineScheduler.h"
>>> +#include "llvm/Support/Debug.h"
>>> +#include "llvm/ADT/PriorityQueue.h"
>>> +
>>> +using namespace llvm;
>>> +
>>> +namespace llvm {
>>> +
>>> +class CompareSUnit {
>>> +public:
>>> +  bool operator()(const SUnit *S1, const SUnit *S2) {
>>> +    return S1->getDepth() > S2->getDepth();
>>> +  }
>>> +};
>>> +
>>> +class R600SchedStrategy : public MachineSchedStrategy {
>>> +
>>> +  const ScheduleDAGMI *DAG;
>>> +  const R600InstrInfo *TII;
>>> +  const R600RegisterInfo *TRI;
>>> +  MachineRegisterInfo *MRI;
>>> +
>>> +  enum InstQueue {
>>> +    QAlu = 1,
>>> +    QFetch = 2,
>>> +    QOther = 4
>>> +  };
>>> +
>>> +  enum InstKind {
>>> +    IDAlu,
>>> +    IDFetch,
>>> +    IDOther,
>>> +    IDLast
>>> +  };
>>> +
>>> +  enum AluKind {
>>> +    AluAny,
>>> +    AluT_X,
>>> +    AluT_Y,
>>> +    AluT_Z,
>>> +    AluT_W,
>>> +    AluT_XYZW,
>>> +    AluDiscarded, // LLVM Instructions that are going to be eliminated
>>> +    AluLast
>>> +  };
>>> +
>>> +  ReadyQueue *Available[IDLast], *Pending[IDLast];
>>> +  std::multiset<SUnit *, CompareSUnit> AvailableAlus[AluLast];
>>> +
>>> +  InstKind CurInstKind;
>>> +  int CurEmitted;
>>> +  InstKind NextInstKind;
>>> +
>>> +  int InstKindLimit[IDLast];
>>> +
>>> +  int OccupedSlotsMask;
>>> +
>>> +public:
>>> +  R600SchedStrategy() :
>>> +    DAG(0), TII(0), TRI(0), MRI(0) {
>>> +    Available[IDAlu] = new ReadyQueue(QAlu, "AAlu");
>>> +    Available[IDFetch] = new ReadyQueue(QFetch, "AFetch");
>>> +    Available[IDOther] = new ReadyQueue(QOther, "AOther");
>>> +    Pending[IDAlu] = new ReadyQueue(QAlu<<4, "PAlu");
>>> +    Pending[IDFetch] = new ReadyQueue(QFetch<<4, 
>> "PFetch");
>>> +    Pending[IDOther] = new ReadyQueue(QOther<<4, 
>> "POther");
>>> +  }
>>> +
>>> +  virtual ~R600SchedStrategy() {
>>> +    for (unsigned I = 0; I < IDLast; ++I) {
>>> +      delete Available[I];
>>> +      delete Pending[I];
>>> +    }
>>> +  }
>>> +
>>> +  virtual void initialize(ScheduleDAGMI *dag);
>>> +  virtual SUnit *pickNode(bool &IsTopNode);
>>> +  virtual void schedNode(SUnit *SU, bool IsTopNode);
>>> +  virtual void releaseTopNode(SUnit *SU);
>>> +  virtual void releaseBottomNode(SUnit *SU);
>>> +
>>> +private:
>>> +  SUnit *InstructionsGroupCandidate[4];
>>> +
>>> +  int getInstKind(SUnit *SU);
>>> +  bool regBelongsToClass(unsigned Reg, const TargetRegisterClass *RC) 
>> const;
>>> +  AluKind getAluKind(SUnit *SU) const;
>>> +  void LoadAlu();
>>> +  bool isAvailablesAluEmpty() const;
>>> +  SUnit *AttemptFillSlot (unsigned Slot);
>>> +  void PrepareNextSlot();
>>> +  SUnit *PopInst(std::multiset<SUnit *, CompareSUnit> &Q);
>>> +
>>> +  void AssignSlot(MachineInstr *MI, unsigned Slot);
>>> +  SUnit* pickAlu();
>>> +  SUnit* pickOther(int QID);
>>> +  bool isBundleable(const MachineInstr& MI);
>>> +  void MoveUnits(ReadyQueue *QSrc, ReadyQueue *QDst);
>>> +};
>>> +
>>> +} // namespace llvm
>>> +
>>> +#endif /* R600MACHINESCHEDULER_H_ */
>> 
>> Hi Vincent,
>> 
>> I only did a quick review, but it looks great.
>> 
>> Please keep in mind that the MachineScheduler is still under development. Unit 
>> tests will prevent future changes from breaking your target too. I know it's 
>> extremely hard to write scheduler unit tests but you may be able to come up with 
>> a few.
>> 
>> -Andy
>> 





More information about the llvm-commits mailing list