//=-- SystemZHazardRecognizer.h - SystemZ Hazard Recognizer -----*- 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
//
//===----------------------------------------------------------------------===//
//
// This file declares a hazard recognizer for the SystemZ scheduler.
//
// This class is used by the SystemZ scheduling strategy to maintain
// the state during scheduling, and provide cost functions for
// scheduling candidates. This includes:
//
// * Decoder grouping. A decoder group can maximally hold 3 uops, and
// instructions that always begin a new group should be scheduled when
// the current decoder group is empty.
// * Processor resources usage. It is beneficial to balance the use of
// resources.
//
// A goal is to consider all instructions, also those outside of any
// scheduling region. Such instructions are "advanced" past and include
// single instructions before a scheduling region, branches etc.
//
// A block that has only one predecessor continues scheduling with the state
// of it (which may be updated by emitting branches).
//
// ===---------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZHAZARDRECOGNIZER_H
#define LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZHAZARDRECOGNIZER_H

#include "SystemZSubtarget.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineScheduler.h"
#include "llvm/CodeGen/ScheduleHazardRecognizer.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/Support/raw_ostream.h"
#include <string>

namespace llvm {

/// SystemZHazardRecognizer maintains the state for one MBB during scheduling.
class SystemZHazardRecognizer : public ScheduleHazardRecognizer {

  const SystemZInstrInfo *TII;
  const TargetSchedModel *SchedModel;

  /// Keep track of the number of decoder slots used in the current
  /// decoder group.
  unsigned CurrGroupSize;

  /// True if an instruction with four reg operands have been scheduled into
  /// the current decoder group.
  bool CurrGroupHas4RegOps;

  /// The tracking of resources here are quite similar to the common
  /// code use of a critical resource. However, z13 differs in the way
  /// that it has two processor sides which may be interesting to
  /// model in the future (a work in progress).

  /// Counters for the number of uops scheduled per processor
  /// resource.
  SmallVector<int, 0> ProcResourceCounters;

  /// This is the resource with the greatest queue, which the
  /// scheduler tries to avoid.
  unsigned CriticalResourceIdx;

  /// Return the number of decoder slots MI requires.
  inline unsigned getNumDecoderSlots(SUnit *SU) const;

  /// Return true if MI fits into current decoder group.
  bool fitsIntoCurrentGroup(SUnit *SU) const;

  /// Return true if this instruction has four register operands.
  bool has4RegOps(const MachineInstr *MI) const;

  /// Two decoder groups per cycle are formed (for z13), meaning 2x3
  /// instructions. This function returns a number between 0 and 5,
  /// representing the current decoder slot of the current cycle.  If an SU
  /// is passed which will begin a new decoder group, the returned value is
  /// the cycle index of the next group.
  unsigned getCurrCycleIdx(SUnit *SU = nullptr) const;

  /// LastFPdOpCycleIdx stores the numbeer returned by getCurrCycleIdx()
  /// when a stalling operation is scheduled (which uses the FPd resource).
  unsigned LastFPdOpCycleIdx;

  /// A counter of decoder groups scheduled.
  unsigned GrpCount;

  unsigned getCurrGroupSize() {return CurrGroupSize;};

  /// Start next decoder group.
  void nextGroup();

  /// Clear all counters for processor resources.
  void clearProcResCounters();

  /// With the goal of alternating processor sides for stalling (FPd)
  /// ops, return true if it seems good to schedule an FPd op next.
  bool isFPdOpPreferred_distance(SUnit *SU) const;

  /// Last emitted instruction or nullptr.
  MachineInstr *LastEmittedMI;

public:
  SystemZHazardRecognizer(const SystemZInstrInfo *tii,
                          const TargetSchedModel *SM)
      : TII(tii), SchedModel(SM) {
    Reset();
  }

  HazardType getHazardType(SUnit *SU, int Stalls = 0) override;
  void Reset() override;
  void EmitInstruction(SUnit *SU) override;

  /// Resolves and cache a resolved scheduling class for an SUnit.
  const MCSchedClassDesc *getSchedClass(SUnit *SU) const {
    if (!SU->SchedClass && SchedModel->hasInstrSchedModel())
      SU->SchedClass = SchedModel->resolveSchedClass(SU->getInstr());
    return SU->SchedClass;
  }

  /// Wrap a non-scheduled instruction in an SU and emit it.
  void emitInstruction(MachineInstr *MI, bool TakenBranch = false);

  // Cost functions used by SystemZPostRASchedStrategy while
  // evaluating candidates.

  /// Return the cost of decoder grouping for SU. If SU must start a
  /// new decoder group, this is negative if this fits the schedule or
  /// positive if it would mean ending a group prematurely. For normal
  /// instructions this returns 0.
  int groupingCost(SUnit *SU) const;

  /// Return the cost of SU in regards to processor resources usage.
  /// A positive value means it would be better to wait with SU, while
  /// a negative value means it would be good to schedule SU next.
  int resourcesCost(SUnit *SU);

#ifndef NDEBUG
  // Debug dumping.
  std::string CurGroupDbg; // current group as text
  void dumpSU(SUnit *SU, raw_ostream &OS) const;
  void dumpCurrGroup(std::string Msg = "") const;
  void dumpProcResourceCounters() const;
  void dumpState() const;
#endif

  MachineBasicBlock::iterator getLastEmittedMI() { return LastEmittedMI; }

  /// Copy counters from end of single predecessor.
  void copyState(SystemZHazardRecognizer *Incoming);
};

} // namespace llvm

#endif /* LLVM_LIB_TARGET_SYSTEMZ_SYSTEMZHAZARDRECOGNIZER_H */
