text: //===--------------------- RegisterFile.h -----------------------*- 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
  //
  //===----------------------------------------------------------------------===//
  /// \file
  ///
  /// This file defines a register mapping file class.  This class is responsible
  /// for managing hardware register files and the tracking of data dependencies
  /// between registers.
  ///
  //===----------------------------------------------------------------------===//
  
  #ifndef LLVM_MCA_REGISTER_FILE_H
  #define LLVM_MCA_REGISTER_FILE_H
  
  #include "llvm/ADT/APInt.h"
  #include "llvm/ADT/SmallVector.h"
  #include "llvm/MC/MCRegisterInfo.h"
  #include "llvm/MC/MCSchedule.h"
  #include "llvm/MC/MCSubtargetInfo.h"
  #include "llvm/MCA/HardwareUnits/HardwareUnit.h"
  #include "llvm/Support/Error.h"
  
  namespace llvm {
  namespace mca {
  
  class ReadState;
  class WriteState;
  class WriteRef;
  
  /// Manages hardware register files, and tracks register definitions for
  /// register renaming purposes.
  class RegisterFile : public HardwareUnit {
    const MCRegisterInfo &MRI;
  
    // class RegisterMappingTracker is a  physical register file (PRF) descriptor.
    // There is one RegisterMappingTracker for every PRF definition in the
    // scheduling model.
    //
    // An instance of RegisterMappingTracker tracks the number of physical
    // registers available for renaming. It also tracks  the number of register
    // moves eliminated per cycle.
    struct RegisterMappingTracker {
      // The total number of physical registers that are available in this
      // register file for register renaming purpouses.  A value of zero for this
      // field means: this register file has an unbounded number of physical
      // registers.
      const unsigned NumPhysRegs;
      // Number of physical registers that are currently in use.
      unsigned NumUsedPhysRegs;
  
      // Maximum number of register moves that can be eliminated by this PRF every
      // cycle. A value of zero means that there is no limit in the number of
      // moves which can be eliminated every cycle.
      const unsigned MaxMoveEliminatedPerCycle;
  
      // Number of register moves eliminated during this cycle.
      //
      // This value is increased by one every time a register move is eliminated.
      // Every new cycle, this value is reset to zero.
      // A move can be eliminated only if MaxMoveEliminatedPerCycle is zero, or if
      // NumMoveEliminated is less than MaxMoveEliminatedPerCycle.
      unsigned NumMoveEliminated;
  
      // If set, move elimination is restricted to zero-register moves only.
      bool AllowZeroMoveEliminationOnly;
  
      RegisterMappingTracker(unsigned NumPhysRegisters,
                             unsigned MaxMoveEliminated = 0U,
                             bool AllowZeroMoveElimOnly = false)
          : NumPhysRegs(NumPhysRegisters), NumUsedPhysRegs(0),
            MaxMoveEliminatedPerCycle(MaxMoveEliminated), NumMoveEliminated(0U),
            AllowZeroMoveEliminationOnly(AllowZeroMoveElimOnly) {}
    };
  
    // A vector of register file descriptors.  This set always contains at least
    // one entry. Entry at index #0 is reserved.  That entry describes a register
    // file with an unbounded number of physical registers that "sees" all the
    // hardware registers declared by the target (i.e. all the register
    // definitions in the target specific `XYZRegisterInfo.td` - where `XYZ` is
    // the target name).
    //
    // Users can limit the number of physical registers that are available in
    // regsiter file #0 specifying command line flag `-register-file-size=<uint>`.
    SmallVector<RegisterMappingTracker, 4> RegisterFiles;
  
    // This type is used to propagate information about the owner of a register,
    // and the cost of allocating it in the PRF. Register cost is defined as the
    // number of physical registers consumed by the PRF to allocate a user
    // register.
    //
    // For example: on X86 BtVer2, a YMM register consumes 2 128-bit physical
    // registers. So, the cost of allocating a YMM register in BtVer2 is 2.
    using IndexPlusCostPairTy = std::pair<unsigned, unsigned>;
  
    // Struct RegisterRenamingInfo is used to map logical registers to register
    // files.
    //
    // There is a RegisterRenamingInfo object for every logical register defined
    // by the target. RegisteRenamingInfo objects are stored into vector
    // `RegisterMappings`, and MCPhysReg IDs can be used to reference
    // elements in that vector.
    //
    // Each RegisterRenamingInfo is owned by a PRF, and field `IndexPlusCost`
    // specifies both the owning PRF, as well as the number of physical registers
    // consumed at register renaming stage.
    //
    // Field `AllowMoveElimination` is set for registers that are used as
    // destination by optimizable register moves.
    //
    // Field `AliasRegID` is set by writes from register moves that have been
    // eliminated at register renaming stage. A move eliminated at register
    // renaming stage is effectively bypassed, and its write aliases the source
    // register definition.
    struct RegisterRenamingInfo {
      IndexPlusCostPairTy IndexPlusCost;
      MCPhysReg RenameAs;
      MCPhysReg AliasRegID;
      bool AllowMoveElimination;
      RegisterRenamingInfo()
          : IndexPlusCost(std::make_pair(0U, 1U)), RenameAs(0U), AliasRegID(0U),
            AllowMoveElimination(false) {}
    };
  
    // RegisterMapping objects are mainly used to track physical register
    // definitions and resolve data dependencies.
    //
    // Every register declared by the Target is associated with an instance of
    // RegisterMapping. RegisterMapping objects keep track of writes to a logical
    // register.  That information is used by class RegisterFile to resolve data
    // dependencies, and correctly set latencies for register uses.
    //
    // This implementation does not allow overlapping register files. The only
    // register file that is allowed to overlap with other register files is
    // register file #0. If we exclude register #0, every register is "owned" by
    // at most one register file.
    using RegisterMapping = std::pair<WriteRef, RegisterRenamingInfo>;
  
    // There is one entry per each register defined by the target.
    std::vector<RegisterMapping> RegisterMappings;
  
    // Used to track zero registers. There is one bit for each register defined by
    // the target. Bits are set for registers that are known to be zero.
    APInt ZeroRegisters;
  
    // This method creates a new register file descriptor.
    // The new register file owns all of the registers declared by register
    // classes in the 'RegisterClasses' set.
    //
    // Processor models allow the definition of RegisterFile(s) via tablegen. For
    // example, this is a tablegen definition for a x86 register file for
    // XMM[0-15] and YMM[0-15], that allows up to 60 renames (each rename costs 1
    // physical register).
    //
    //    def FPRegisterFile : RegisterFile<60, [VR128RegClass, VR256RegClass]>
    //
    // Here FPRegisterFile contains all the registers defined by register class
    // VR128RegClass and VR256RegClass. FPRegisterFile implements 60
    // registers which can be used for register renaming purpose.
    void addRegisterFile(const MCRegisterFileDesc &RF,
                         ArrayRef<MCRegisterCostEntry> Entries);
  
    // Consumes physical registers in each register file specified by the
    // `IndexPlusCostPairTy`. This method is called from `addRegisterMapping()`.
    void allocatePhysRegs(const RegisterRenamingInfo &Entry,
                          MutableArrayRef<unsigned> UsedPhysRegs);
  
    // Releases previously allocated physical registers from the register file(s).
    // This method is called from `invalidateRegisterMapping()`.
    void freePhysRegs(const RegisterRenamingInfo &Entry,
                      MutableArrayRef<unsigned> FreedPhysRegs);
  
    // Collects writes that are in a RAW dependency with RS.
    // This method is called from `addRegisterRead()`.
    void collectWrites(const ReadState &RS,
                       SmallVectorImpl<WriteRef> &Writes) const;
  
    // Create an instance of RegisterMappingTracker for every register file
    // specified by the processor model.
    // If no register file is specified, then this method creates a default
    // register file with an unbounded number of physical registers.
    void initialize(const MCSchedModel &SM, unsigned NumRegs);
  
  public:
    RegisterFile(const MCSchedModel &SM, const MCRegisterInfo &mri,
                 unsigned NumRegs = 0);
  
    // This method updates the register mappings inserting a new register
    // definition. This method is also responsible for updating the number of
    // allocated physical registers in each register file modified by the write.
    // No physical regiser is allocated if this write is from a zero-idiom.
    void addRegisterWrite(WriteRef Write, MutableArrayRef<unsigned> UsedPhysRegs);
  
    // Collect writes that are in a data dependency with RS, and update RS
    // internal state.
    void addRegisterRead(ReadState &RS, const MCSubtargetInfo &STI) const;
  
    // Removes write \param WS from the register mappings.
    // Physical registers may be released to reflect this update.
    // No registers are released if this write is from a zero-idiom.
    void removeRegisterWrite(const WriteState &WS,
                             MutableArrayRef<unsigned> FreedPhysRegs);
  
    // Returns true if a move from RS to WS can be eliminated.
    // On success, it updates WriteState by setting flag `WS.isEliminated`.
    // If RS is a read from a zero register, and WS is eliminated, then
    // `WS.WritesZero` is also set, so that method addRegisterWrite() would not
    // reserve a physical register for it.
    bool tryEliminateMove(WriteState &WS, ReadState &RS);
  
    // Checks if there are enough physical registers in the register files.
    // Returns a "response mask" where each bit represents the response from a
    // different register file.  A mask of all zeroes means that all register
    // files are available.  Otherwise, the mask can be used to identify which
    // register file was busy.  This sematic allows us to classify dispatch
    // stalls caused by the lack of register file resources.
    //
    // Current implementation can simulate up to 32 register files (including the
    // special register file at index #0).
    unsigned isAvailable(ArrayRef<MCPhysReg> Regs) const;
  
    // Returns the number of PRFs implemented by this processor.
    unsigned getNumRegisterFiles() const { return RegisterFiles.size(); }
  
    // Notify each PRF that a new cycle just started.
    void cycleStart();
  
  #ifndef NDEBUG
    void dump() const;
  #endif
  };
  
  } // namespace mca
  } // namespace llvm
  
  #endif // LLVM_MCA_REGISTER_FILE_H
  