//===- ARMELFAttributeData.h ----------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef TARGET_ARM_ARMELFATTRIBUTEDATA_H_
#define TARGET_ARM_ARMELFATTRIBUTEDATA_H_

#include "mcld/Target/ELFAttributeData.h"
#include "mcld/Target/ELFAttributeValue.h"

#include <map>
#include <string>

namespace mcld {

/** \class ARMELFAttributeData
 *  \brief ARMELFAttributeData handles public ("aeabi") attributes subsection in
 *  ARM ELF.
 *
 */
class ARMELFAttributeData : public ELFAttributeData {
 public:
  enum Tag {
    // 0-3 are generic and are defined in ELFAttributeData.
    Tag_CPU_raw_name = 4,
    Tag_CPU_name = 5,
    Tag_CPU_arch = 6,
    Tag_CPU_arch_profile = 7,
    Tag_ARM_ISA_use = 8,
    Tag_THUMB_ISA_use = 9,
    Tag_FP_arch = 10,
    Tag_WMMX_arch = 11,
    Tag_Advanced_SIMD_arch = 12,
    Tag_PCS_config = 13,
    Tag_ABI_PCS_R9_use = 14,
    Tag_ABI_PCS_RW_data = 15,
    Tag_ABI_PCS_RO_data = 16,
    Tag_ABI_PCS_GOT_use = 17,
    Tag_ABI_PCS_wchar_t = 18,
    Tag_ABI_FP_rounding = 19,
    Tag_ABI_FP_denormal = 20,
    Tag_ABI_FP_exceptions = 21,
    Tag_ABI_FP_user_exceptions = 22,
    Tag_ABI_FP_number_model = 23,
    Tag_ABI_align_needed = 24,
    Tag_ABI_align_preserved = 25,
    Tag_ABI_enum_size = 26,
    Tag_ABI_HardFP_use = 27,
    Tag_ABI_VFP_args = 28,
    Tag_ABI_WMMX_args = 29,
    Tag_ABI_optimization_goals = 30,
    Tag_ABI_FP_optimization_goals = 31,
    Tag_compatibility = 32,

    Tag_CPU_unaligned_access = 34,

    Tag_FP_HP_extension = 36,

    Tag_ABI_FP_16bit_format = 38,

    Tag_MPextension_use = 42,

    Tag_DIV_use = 44,

    Tag_nodefaults = 64,
    Tag_also_compatible_with = 65,
    Tag_T2EE_use = 66,
    Tag_conformance = 67,
    Tag_Virtualization_use = 68,

    Tag_MPextension_use_legacy = 70,

    Tag_Max = Tag_MPextension_use_legacy,

    // Alias
    Tag_VFP_arch = Tag_FP_arch,
    Tag_ABI_align8_needed = Tag_ABI_align_needed,
    Tag_ABI_align8_preserved = Tag_ABI_align_preserved,
    Tag_VFP_HP_extension = Tag_FP_HP_extension
  };

  // For Tag_CPU_arch
  enum {
    CPU_Arch_ARM_Pre_V4,
    CPU_Arch_ARM_V4,     // e.g., SA110
    CPU_Arch_ARM_V4T,    // e.g., ARM7TDMI
    CPU_Arch_ARM_V5T,    // e.g., ARM9TDMI
    CPU_Arch_ARM_V5TE,   // e.g., ARM946E-S
    CPU_Arch_ARM_V5TEJ,  // e.g., ARM926EJ-S
    CPU_Arch_ARM_V6,     // e.g., ARM1136J-S
    CPU_Arch_ARM_V6KZ,   // e.g., ARM1176JZ-S
    CPU_Arch_ARM_V6T2,   // e.g., ARM1156T2F-S
    CPU_Arch_ARM_V6K,    // e.g., ARM1136J-S
    CPU_Arch_ARM_V7,     // e.g., Cortex A8, Cortex M3
    CPU_Arch_ARM_V6_M,   // e.g., Cortex M1
    CPU_Arch_ARM_V6S_M,  // e.g., v6-M with the value of System extensions
    CPU_Arch_ARM_V7E_M,  // e.g., v7-M with DSP extensions
    CPU_Arch_ARM_V8,

    CPU_Arch_Max = CPU_Arch_ARM_V8,

    // This is a pseudo-architecture to describe an architecture mixed with
    // the subset of armv4t and armv6-m. This never appears in the value of
    // Tag_CPU_arch.
    CPU_Arch_ARM_V4T_Plus_V6_M = (CPU_Arch_Max + 1),

    CPU_Arch_Plus_Pseudo_Max = CPU_Arch_ARM_V4T_Plus_V6_M,
  };

  // For Tag_CPU_arch_profile
  enum {
    Arch_Profile_None = 0,
    Arch_Profile_Application = 'A',
    Arch_Profile_Realtime = 'R',
    Arch_Profile_Microcontroller = 'M',
    Arch_Profile_RealOrApp = 'S'
  };

  // For Tag_ABI_enum_size
  enum {
    Enum_Unused,
    Enum_Smallest_Container,
    Enum_32bit_Container,
    Enum_Containerized_As_Possible
  };

  // For Tag_ABI_PCS_R9_use
  enum { R9_V6, R9_SB, R9_TLS, R9_Unused };

  // For Tag_ABI_PCS_RW_data
  enum {
    RW_data_Absolute,
    RW_data_PC_Relative,
    RW_data_SB_Relative,
    RW_data_unused
  };

 public:
  // ARM [ABI-addenda], 2.2.2: A public attributes subsection is named aeabi.
  ARMELFAttributeData()
      : ELFAttributeData("aeabi"),
        m_CurrentCPUArch(-1),
        m_DIVUseInitialized(false),
        m_HardFPUseInitialized(false) {}

 public:
  virtual const ELFAttributeValue* getAttributeValue(TagType pTag) const;

  virtual std::pair<ELFAttributeValue*, bool> getOrCreateAttributeValue(
      TagType pTag);

  virtual bool preMerge(const Input& pInput) {
    // Reset states.
    m_CPUArch = -1;
    m_CPUName.clear();
    m_CPURawName.clear();
    m_SecondaryCPUArch = -1;
    m_VFPArgs = -1;
    m_FPArch = -1;
    m_HardFPUse = -1;
    m_MPextensionUse = -1;
    m_DIVUse = -1;
    return true;
  }

  virtual bool merge(const LinkerConfig& pConfig,
                     const Input& pInput,
                     TagType pTag,
                     const ELFAttributeValue& pInAttr);

  virtual bool postMerge(const LinkerConfig& pConfig, const Input& pInput);

  virtual size_t sizeOutput() const;

  virtual size_t emit(char* pBuf) const;

  virtual bool usingThumb() const;

  virtual bool usingThumb2() const;

 private:
  /// GetAttributeValueType - obtain the value type of the indicated tag.
  static unsigned int GetAttributeValueType(TagType pTag);

 private:
  // The storage for known tags which is indexed by the tag
  ELFAttributeValue m_Attrs[Tag_Max + 1];

  // The storage for unknown tags
  typedef std::map<TagType, ELFAttributeValue> UnknownAttrsMap;
  UnknownAttrsMap m_UnknownAttrs;

  // This is a cache for the current output architecture calculate from of
  // Tag_CPU_arch and Tag_also_compatible_with.
  int m_CurrentCPUArch;

  // Value of Tag_DIV_use and Tag_ABI_HardFP_use requires further examination
  // for the every time adding to the output. These booleans are initialized to
  // false and set to true until the corresponding attribute is initialized.
  bool m_DIVUseInitialized;
  bool m_HardFPUseInitialized;

  // These attributes have dependency with each other. During the merge, we
  // record their attribute values in the associated variables as follows and
  // process them in postmerge() (when all other attributes are settled down.)

  // Record the value of input Tag_CPU_arch.
  int m_CPUArch;

  // Record the value of input Tag_CPU_name.
  std::string m_CPUName;

  // Record the value of input Tag_CPU_raw_name.
  std::string m_CPURawName;

  // Record the value of input Tag_FP_arch.
  int m_FPArch;

  // Record the value of input Tag_ABI_HardFP_use.
  int m_HardFPUse;

  // Record the value of input Tag_also_compatible_with.
  int m_SecondaryCPUArch;

  // Record the value of input Tag_ABI_VFP_args.
  int m_VFPArgs;

  // Record the value of input Tag_MPextension_use and
  // Tag_MPextension_use_legacy.
  int m_MPextensionUse;

  // Record the value of input Tag_DIV_use.
  int m_DIVUse;
};

}  // namespace mcld

#endif  // TARGET_ARM_ARMELFATTRIBUTEDATA_H_