//===-- PTXMachineFuctionInfo.h - PTX machine function info ------*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares PTX-specific per-machine-function information.
//
//===----------------------------------------------------------------------===//

#ifndef PTX_MACHINE_FUNCTION_INFO_H
#define PTX_MACHINE_FUNCTION_INFO_H

#include "PTX.h"
#include "PTXParamManager.h"
#include "PTXRegisterInfo.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

namespace llvm {

/// PTXMachineFunctionInfo - This class is derived from MachineFunction and
/// contains private PTX target-specific information for each MachineFunction.
///
class PTXMachineFunctionInfo : public MachineFunctionInfo {
  virtual void anchor();
  bool IsKernel;
  DenseSet<unsigned> RegArgs;
  DenseSet<unsigned> RegRets;

  typedef DenseMap<int, std::string> FrameMap;

  FrameMap FrameSymbols;

  struct RegisterInfo {
    unsigned Reg;
    unsigned Type;
    unsigned Space;
    unsigned Offset;
    unsigned Encoded;
  };

  typedef DenseMap<unsigned, RegisterInfo> RegisterInfoMap;

  RegisterInfoMap RegInfo;

  PTXParamManager ParamManager;

public:
  typedef DenseSet<unsigned>::const_iterator reg_iterator;

  PTXMachineFunctionInfo(MachineFunction &MF)
    : IsKernel(false) {
  }

  /// getParamManager - Returns the PTXParamManager instance for this function.
  PTXParamManager& getParamManager() { return ParamManager; }
  const PTXParamManager& getParamManager() const { return ParamManager; }

  /// setKernel/isKernel - Gets/sets a flag that indicates if this function is
  /// a PTX kernel function.
  void setKernel(bool _IsKernel=true) { IsKernel = _IsKernel; }
  bool isKernel() const { return IsKernel; }

  /// argreg_begin/argreg_end - Returns iterators to the set of registers
  /// containing function arguments.
  reg_iterator argreg_begin() const { return RegArgs.begin(); }
  reg_iterator argreg_end()   const { return RegArgs.end(); }

  /// retreg_begin/retreg_end - Returns iterators to the set of registers
  /// containing the function return values.
  reg_iterator retreg_begin() const { return RegRets.begin(); }
  reg_iterator retreg_end()   const { return RegRets.end(); }

  /// addRegister - Adds a virtual register to the set of all used registers
  void addRegister(unsigned Reg, unsigned RegType, unsigned RegSpace) {
    if (!RegInfo.count(Reg)) {
      RegisterInfo Info;
      Info.Reg = Reg;
      Info.Type = RegType;
      Info.Space = RegSpace;

      // Determine register offset
      Info.Offset = 0;
      for(RegisterInfoMap::const_iterator i = RegInfo.begin(),
          e = RegInfo.end(); i != e; ++i) {
        const RegisterInfo& RI = i->second;
        if (RI.Space == RegSpace)
          if (RI.Space != PTXRegisterSpace::Reg || RI.Type == Info.Type)
            Info.Offset++;
      }

      // Encode the register data into a single register number
      Info.Encoded = (Info.Offset << 6) | (Info.Type << 3) | Info.Space;

      RegInfo[Reg] = Info;

      if (RegSpace == PTXRegisterSpace::Argument)
        RegArgs.insert(Reg);
      else if (RegSpace == PTXRegisterSpace::Return)
        RegRets.insert(Reg);
    }
  }

  /// countRegisters - Returns the number of registers of the given type and
  /// space.
  unsigned countRegisters(unsigned RegType, unsigned RegSpace) const {
    unsigned Count = 0;
    for(RegisterInfoMap::const_iterator i = RegInfo.begin(), e = RegInfo.end();
        i != e; ++i) {
      const RegisterInfo& RI = i->second;
      if (RI.Type == RegType && RI.Space == RegSpace)
        Count++;
    }
    return Count;
  }

  /// getEncodedRegister - Returns the encoded value of the register.
  unsigned getEncodedRegister(unsigned Reg) const {
    return RegInfo.lookup(Reg).Encoded;
  }

  /// addRetReg - Adds a register to the set of return-value registers.
  void addRetReg(unsigned Reg) {
    if (!RegRets.count(Reg)) {
      RegRets.insert(Reg);
    }
  }

  /// addArgReg - Adds a register to the set of function argument registers.
  void addArgReg(unsigned Reg) {
    RegArgs.insert(Reg);
  }

  /// getRegisterName - Returns the name of the specified virtual register. This
  /// name is used during PTX emission.
  std::string getRegisterName(unsigned Reg) const {
    if (RegInfo.count(Reg)) {
      const RegisterInfo& RI = RegInfo.lookup(Reg);
      std::string Name;
      raw_string_ostream NameStr(Name);
      decodeRegisterName(NameStr, RI.Encoded);
      NameStr.flush();
      return Name;
    }
    else if (Reg == PTX::NoRegister)
      return "%noreg";
    else
      llvm_unreachable("Register not in register name map");
  }

  /// getEncodedRegisterName - Returns the name of the encoded register.
  std::string getEncodedRegisterName(unsigned EncodedReg) const {
    std::string Name;
    raw_string_ostream NameStr(Name);
    decodeRegisterName(NameStr, EncodedReg);
    NameStr.flush();
    return Name;
  }

  /// getRegisterType - Returns the type of the specified virtual register.
  unsigned getRegisterType(unsigned Reg) const {
    if (RegInfo.count(Reg))
      return RegInfo.lookup(Reg).Type;
    else
      llvm_unreachable("Unknown register");
  }

  /// getOffsetForRegister - Returns the offset of the virtual register
  unsigned getOffsetForRegister(unsigned Reg) const {
    if (RegInfo.count(Reg))
      return RegInfo.lookup(Reg).Offset;
    else
      return 0;
  }

  /// getFrameSymbol - Returns the symbol name for the given FrameIndex.
  const char* getFrameSymbol(int FrameIndex) {
    if (FrameSymbols.count(FrameIndex)) {
      return FrameSymbols.lookup(FrameIndex).c_str();
    } else {
      std::string Name          = "__local";
      Name                     += utostr(FrameIndex);
      // The whole point of caching this name is to ensure the pointer we pass
      // to any getExternalSymbol() calls will remain valid for the lifetime of
      // the back-end instance. This is to work around an issue in SelectionDAG
      // where symbol names are expected to be life-long strings.
      FrameSymbols[FrameIndex]  = Name;
      return FrameSymbols[FrameIndex].c_str();
    }
  }
}; // class PTXMachineFunctionInfo
} // namespace llvm

#endif // PTX_MACHINE_FUNCTION_INFO_H
