//===--------------------- RegisterFile.h -----------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \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_TOOLS_LLVM_MCA_REGISTER_FILE_H
#define LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H

#include "HardwareUnits/HardwareUnit.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSchedule.h"
#include "llvm/Support/Error.h"

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 llvm::MCRegisterInfo &MRI;

  // Each register file is associated with an instance of
  // RegisterMappingTracker.
  // A RegisterMappingTracker keeps track of the number of physical registers
  // which have been dynamically allocated by the simulator.
  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;

    RegisterMappingTracker(unsigned NumPhysRegisters)
        : NumPhysRegs(NumPhysRegisters), NumUsedPhysRegs(0) {}
  };

  // 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>`.
  llvm::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 maps registers to register files.
  // There is a RegisterRenamingInfo object for every register defined by
  // the target. RegisteRenamingInfo objects are stored into vector
  // RegisterMappings, and register IDs can be used to reference them.
  struct RegisterRenamingInfo {
    IndexPlusCostPairTy IndexPlusCost;
    llvm::MCPhysReg RenameAs;
  };

  // RegisterMapping objects are mainly used to track physical register
  // definitions. There is a RegisterMapping for every register defined by the
  // Target. For each register, a RegisterMapping pair contains a descriptor of
  // the last register write (in the form of a WriteRef object), as well as a
  // RegisterRenamingInfo to quickly identify owning register files.
  //
  // 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>;

  // This map contains one entry for each register defined by the target.
  std::vector<RegisterMapping> RegisterMappings;

  // 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(llvm::ArrayRef<llvm::MCRegisterCostEntry> RegisterClasses,
                  unsigned NumPhysRegs);

  // Consumes physical registers in each register file specified by the
  // `IndexPlusCostPairTy`. This method is called from `addRegisterMapping()`.
  void allocatePhysRegs(const RegisterRenamingInfo &Entry,
                        llvm::MutableArrayRef<unsigned> UsedPhysRegs);

  // Releases previously allocated physical registers from the register file(s).
  // This method is called from `invalidateRegisterMapping()`.
  void freePhysRegs(const RegisterRenamingInfo &Entry,
                    llvm::MutableArrayRef<unsigned> FreedPhysRegs);

  // 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 llvm::MCSchedModel &SM, unsigned NumRegs);

public:
  RegisterFile(const llvm::MCSchedModel &SM, const llvm::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 when flag ShouldAllocatePhysRegs is set.
  void addRegisterWrite(WriteRef Write,
                        llvm::MutableArrayRef<unsigned> UsedPhysRegs,
                        bool ShouldAllocatePhysRegs = true);

  // Removes write \param WS from the register mappings.
  // Physical registers may be released to reflect this update.
  void removeRegisterWrite(const WriteState &WS,
                           llvm::MutableArrayRef<unsigned> FreedPhysRegs,
                           bool ShouldFreePhysRegs = true);

  // 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(llvm::ArrayRef<unsigned> Regs) const;
  void collectWrites(llvm::SmallVectorImpl<WriteRef> &Writes,
                     unsigned RegID) const;
  unsigned getNumRegisterFiles() const { return RegisterFiles.size(); }

#ifndef NDEBUG
  void dump() const;
#endif
};

} // namespace mca

#endif // LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
