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

#include "mcld/LinkerConfig.h"
#include "mcld/MC/Input.h"
#include "mcld/Support/LEB128.h"
#include "mcld/Support/MsgHandling.h"
#include <llvm/ADT/STLExtras.h>

namespace mcld {

const ELFAttributeValue* ARMELFAttributeData::getAttributeValue(
    TagType pTag) const {
  if (pTag <= Tag_Max) {
    const ELFAttributeValue& attr_value = m_Attrs[pTag];

    if (attr_value.isInitialized()) {
      return &attr_value;
    } else {
      // Don't return uninitialized attribute value.
      return NULL;
    }
  } else {
    UnknownAttrsMap::const_iterator attr_it = m_UnknownAttrs.find(pTag);

    if (attr_it == m_UnknownAttrs.end()) {
      return NULL;
    } else {
      return &attr_it->second;
    }
  }
}

std::pair<ELFAttributeValue*, bool>
ARMELFAttributeData::getOrCreateAttributeValue(TagType pTag) {
  ELFAttributeValue* attr_value = NULL;

  if (pTag <= Tag_Max) {
    attr_value = &m_Attrs[pTag];
  } else {
    // An unknown tag encounterred.
    attr_value = &m_UnknownAttrs[pTag];
  }

  assert(attr_value != NULL);

  // Setup the value type.
  if (!attr_value->isUninitialized()) {
    return std::make_pair(attr_value, false);
  } else {
    attr_value->setType(GetAttributeValueType(pTag));
    return std::make_pair(attr_value, true);
  }
}

unsigned int ARMELFAttributeData::GetAttributeValueType(TagType pTag) {
  // See ARM [ABI-addenda], 2.2.6.
  switch (pTag) {
    case Tag_compatibility: {
      return (ELFAttributeValue::Int | ELFAttributeValue::String);
    }
    case Tag_nodefaults: {
      return (ELFAttributeValue::Int | ELFAttributeValue::NoDefault);
    }
    case Tag_CPU_raw_name:
    case Tag_CPU_name: {
      return ELFAttributeValue::String;
    }
    default: {
      if (pTag < 32)
        return ELFAttributeValue::Int;
      else
        return ((pTag & 1) ? ELFAttributeValue::String
                           : ELFAttributeValue::Int);
    }
  }
  // unreachable
}

//===--------------------------------------------------------------------===//
// Helper Functions for merge()
//===--------------------------------------------------------------------===//

namespace {

/*
 * Helper function to decode value in Tag_also_compatible_with.
 *
 * @ref ARM [ABI-addenda], 2.3.7.3
 */
static int decode_secondary_compatibility_attribute(
    const ELFAttributeValue& pValue) {
  // The encoding of Tag_also_compatible_with is:
  //
  // Tag_also_compatible_with (=65), NTSB: data
  //
  // The data can be either an ULEB128-encoded number followed by a NULL byte or
  // a NULL-terminated string. Currently, only the following byte sequence in
  // data are currently defined:
  //
  // Tag_CPU_arch (=6) [The arch] 0
  assert((pValue.type() == ELFAttributeValue::String) &&
         "Value of Tag_also_compatible_with must be a string!");

  const std::string& data = pValue.getStringValue();

  // Though the integer is in LEB128 format, but they occupy only 1 byte in
  // currently defined value.
  if (data.length() < 2)
    // Must have a byte for Tag_CPU_arch (=6)
    //           a byte for specifying the CPU architecture (CPU_Arch_ARM_*)
    //
    // Currently, the 2nd byte can only be v4T (=2) or v6-M (=11).
    return -1;

  if ((static_cast<uint8_t>(data[0]) == ARMELFAttributeData::Tag_CPU_arch) &&
      ((data[1] == ARMELFAttributeData::CPU_Arch_ARM_V4T) ||
       (data[1] == ARMELFAttributeData::CPU_Arch_ARM_V6_M)))
    return static_cast<uint32_t>(data[1]);

  // Tag_also_compatible_with can be safely ignored.
  return -1;
}

/*
 * This helper array keeps the ordering of the values in attributes such as
 * Tag_ABI_align_needed which are sored as 1 > 2 > 0.
 */
static const int value_ordering_120[] = {0, 2, 1};

}  // anonymous namespace

//===--------------------------------------------------------------------===//
// End Helper Functions for merge()
//===--------------------------------------------------------------------===//

bool ARMELFAttributeData::merge(const LinkerConfig& pConfig,
                                const Input& pInput,
                                TagType pTag,
                                const ELFAttributeValue& pInAttr) {
  // Pre-condition
  //  1. The out_attr must be initailized and has value of the same type as
  //     pInAttr.
  //  2. The value helf by out_attr and pInAttr must be different.
  ELFAttributeValue& out_attr = m_Attrs[pTag];

  // Attribute in the output must have value assigned.
  assert(out_attr.isInitialized() && "No output attribute to be merged!");

  switch (pTag) {
    case Tag_CPU_arch: {
      // Need value of Tag_also_compatible_with in the input for merge.
      if (pInAttr.getIntValue() <= CPU_Arch_Max) {
        m_CPUArch = pInAttr.getIntValue();
      } else {
        error(diag::error_unknown_cpu_arch) << pInput.name();
        return false;
      }
      break;
    }
    case Tag_CPU_name: {
      // need value of Tag_CPU_arch in the input for merge
      m_CPUName = pInAttr.getStringValue();
      break;
    }
    case Tag_CPU_raw_name: {
      // need value of Tag_CPU_arch in the input for merge
      m_CPURawName = pInAttr.getStringValue();
      break;
    }
    case Tag_FP_arch: {
      // need value of Tag_HardFP_use in the input for merge
      m_FPArch = pInAttr.getIntValue();
      break;
    }
    case Tag_ABI_HardFP_use: {
      // need value of Tag_FP_arch in the input for merge
      m_HardFPUse = pInAttr.getIntValue();
      break;
    }
    case Tag_also_compatible_with: {
      // need value of Tag_CPU_arch in the input for merge
      m_SecondaryCPUArch = decode_secondary_compatibility_attribute(pInAttr);
      break;
    }
    case Tag_ABI_VFP_args: {
      // need value of Tag_ABI_FP_number_model in the input for merge
      m_VFPArgs = pInAttr.getIntValue();
      break;
    }
    // The value of these tags are integers and after merge, only the greatest
    // value held by pInAttr and out_attr goes into output.
    case Tag_ARM_ISA_use:
    case Tag_THUMB_ISA_use:
    case Tag_WMMX_arch:
    case Tag_Advanced_SIMD_arch:
    case Tag_ABI_FP_rounding:
    case Tag_ABI_FP_exceptions:
    case Tag_ABI_FP_user_exceptions:
    case Tag_ABI_FP_number_model:
    case Tag_FP_HP_extension:
    case Tag_CPU_unaligned_access:
    case Tag_T2EE_use: {
      assert((out_attr.type() == ELFAttributeValue::Int) &&
             (pInAttr.type() == ELFAttributeValue::Int) &&
             "should have integer parameeter!");
      if (pInAttr.getIntValue() > out_attr.getIntValue())
        out_attr.setIntValue(pInAttr.getIntValue());
      break;
    }
    // The value of these tags are integers and after merge, only the smallest
    // value held by pInAttr and out_attr goes into output.
    case Tag_ABI_align_preserved:
    case Tag_ABI_PCS_RO_data: {
      assert((out_attr.type() == ELFAttributeValue::Int) &&
             (pInAttr.type() == ELFAttributeValue::Int) &&
             "should have integer parameeter!");
      if (pInAttr.getIntValue() < out_attr.getIntValue())
        out_attr.setIntValue(pInAttr.getIntValue());
      break;
    }
    // The values of these attributes are sorted as 1 > 2 > 0. And the greater
    // value becomes output.
    case Tag_ABI_align_needed:
    case Tag_ABI_FP_denormal:
    case Tag_ABI_PCS_GOT_use: {
      const int in_val = pInAttr.getIntValue();
      const int out_val = out_attr.getIntValue();

      if (in_val <= 2) {
        if (out_val <= 2) {
          // Use value_ordering_120 to determine the ordering.
          if (value_ordering_120[in_val] > value_ordering_120[out_val]) {
            out_attr.setIntValue(in_val);
          }
        }
      } else {
        // input value > 2, for future-proofing
        if (in_val > out_val) {
          out_attr.setIntValue(in_val);
        }
      }
      break;
    }
    // These tags use the first value ever seen.
    case Tag_ABI_optimization_goals:
    case Tag_ABI_FP_optimization_goals: {
      break;
    }
    // Tag_CPU_arch_profile
    case Tag_CPU_arch_profile: {
      if (pInAttr.getIntValue() == Arch_Profile_None)
        return true;

      switch (out_attr.getIntValue()) {
        case Arch_Profile_None: {
          out_attr.setIntValue(pInAttr.getIntValue());
          break;
        }
        case Arch_Profile_RealOrApp: {
          if (pInAttr.getIntValue() != Arch_Profile_Microcontroller)
            out_attr.setIntValue(pInAttr.getIntValue());
          else
            warning(diag::warn_mismatch_cpu_arch_profile)
                << pInAttr.getIntValue() << pInput.name();
          break;
        }
        default: {
          // out_attr is Arch_Profile_Application or Arch_Profile_Realtime or
          // Arch_Profile_Microcontroller.
          if ((pInAttr.getIntValue() == Arch_Profile_RealOrApp) &&
              (out_attr.getIntValue() != Arch_Profile_Microcontroller)) {
            // do nothing
          } else {
            if (pConfig.options().warnMismatch())
              warning(diag::warn_mismatch_cpu_arch_profile)
                  << pInAttr.getIntValue() << pInput.name();
          }
          break;
        }
      }
      break;
    }
    // Tag_MPextension_use and Tag_MPextension_use_legacy
    case Tag_MPextension_use:
    case Tag_MPextension_use_legacy: {
      if (m_MPextensionUse < 0) {
        m_MPextensionUse = pInAttr.getIntValue();
      } else {
        if (static_cast<unsigned>(m_MPextensionUse) != pInAttr.getIntValue()) {
          warning(diag::error_mismatch_mpextension_use) << pInput.name();
        }
      }
      break;
    }
    // Tag_DIV_use
    case Tag_DIV_use: {
      if (pInAttr.getIntValue() == 2) {
        // 2 means the code was permitted to use SDIV/UDIV in anyway.
        out_attr.setIntValue(2);
      } else {
        // Merge until settling down Tag_CPU_arch.
        m_DIVUse = pInAttr.getIntValue();
      }
      break;
    }
    // Tag_ABI_enum_size
    case Tag_ABI_enum_size: {
      if ((out_attr.getIntValue() == Enum_Unused) ||
          (out_attr.getIntValue() == Enum_Containerized_As_Possible))
        out_attr.setIntValue(pInAttr.getIntValue());
      else if (pInAttr.getIntValue() != Enum_Containerized_As_Possible &&
               pConfig.options().warnMismatch())
        warning(diag::warn_mismatch_enum_size)
            << pInput.name() << pInAttr.getIntValue() << out_attr.getIntValue();
      break;
    }
    // Tag_ABI_FP_16bit_format
    case Tag_ABI_FP_16bit_format: {
      // 0: doesn't use any 16-bit FP number
      // 1: use IEEE 754 format 16-bit FP number
      // 2: use VFPv3/Advanced SIMD "alternative format" 16-bit FP number
      if (pInAttr.getIntValue() != 0) {
        if (out_attr.getIntValue() == 0) {
          out_attr.setIntValue(pInAttr.getIntValue());
        } else {
          if (pConfig.options().warnMismatch())
            warning(diag::warn_mismatch_fp16_format) << pInput.name();
        }
      }
      break;
    }
    // Tag_nodefaults
    case Tag_nodefaults: {
      // There's nothing to do for this tag. It doesn't have an actual value.
      break;
    }
    // Tag_conformance
    case Tag_conformance: {
      // Throw away the value if the attribute value doesn't match.
      if (out_attr.getStringValue() != pInAttr.getStringValue())
        out_attr.setStringValue("");
      break;
    }
    // Tag_Virtualization_use
    case Tag_Virtualization_use: {
      // 0: No use of any virtualization extension
      // 1: TrustZone
      // 2: Virtualization extension such as HVC and ERET
      // 3: TrustZone and virtualization extension are permitted
      if (pInAttr.getIntValue() != 0) {
        if (out_attr.getIntValue() == 0) {
          out_attr.setIntValue(pInAttr.getIntValue());
        } else {
          if ((out_attr.getIntValue() <= 3) && (pInAttr.getIntValue() <= 3)) {
            // Promote to 3
            out_attr.setIntValue(3);
          } else {
            warning(diag::warn_unrecognized_virtualization_use)
                << pInput.name() << pInAttr.getIntValue();
          }
        }
      }
      break;
    }
    // Tag_ABI_WMMX_args
    case Tag_ABI_WMMX_args: {
      // There's no way to merge this value (i.e., objects contain different
      // value in this tag are definitely incompatible.)
      if (pConfig.options().warnMismatch())
        warning(diag::warn_mismatch_abi_wmmx_args) << pInput.name();
      break;
    }
    // Tag_PCS_config
    case Tag_PCS_config: {
      // 0 means no standard configuration used or no information recorded.
      if (pInAttr.getIntValue() != 0) {
        if (out_attr.getIntValue() == 0)
          out_attr.setIntValue(pInAttr.getIntValue());
        else {
          // Different values in these attribute are conflict
          if (pConfig.options().warnMismatch())
            warning(diag::warn_mismatch_pcs_config) << pInput.name();
        }
      }
      break;
    }
    // Tag_ABI_PCS_R9_use
    case Tag_ABI_PCS_R9_use: {
      if (pInAttr.getIntValue() != R9_Unused) {
        if (out_attr.getIntValue() == R9_Unused)
          out_attr.setIntValue(pInAttr.getIntValue());
        else {
          if (pConfig.options().warnMismatch())
            warning(diag::warn_mismatch_r9_use) << pInput.name();
        }
      }
      break;
    }
    // Tag_ABI_PCS_RW_data
    case Tag_ABI_PCS_RW_data: {
      if (pInAttr.getIntValue() == RW_data_SB_Relative) {
        // Require using R9 as SB (global Static Base register).
        if ((out_attr.getIntValue() != R9_Unused) &&
            (out_attr.getIntValue() != R9_SB) &&
            pConfig.options().warnMismatch())
          warning(diag::warn_mismatch_r9_use) << pInput.name();
      }
      // Choose the smaller value
      if (pInAttr.getIntValue() < out_attr.getIntValue())
        out_attr.setIntValue(pInAttr.getIntValue());
      break;
    }
    // Tag_ABI_PCS_wchar_t
    case Tag_ABI_PCS_wchar_t: {
      // 0: no use of wchar_t
      // 2: sizeof(wchar_t) = 2
      // 4: sizeof(wchar_t) = 4
      if (pInAttr.getIntValue() != 0) {
        if (out_attr.getIntValue() == 0)
          out_attr.setIntValue(pInAttr.getIntValue());
        else {
          if (pConfig.options().warnMismatch())
            warning(diag::warn_mismatch_wchar_size) << pInput.name()
                                                    << pInAttr.getIntValue()
                                                    << out_attr.getIntValue();
        }
      }
      break;
    }
    default: {
      // Handle unknown attributes:
      //
      // Since we don't know how to merge the value of unknown attribute, we
      // have to ignore it. There're two rules related to the processing (See
      // ARM [ABI-addenda] 2.2.6, Coding extensibility and compatibility.):
      //
      // 1. For tag N where N >= 128, tag N has the same properties as
      //    tag N % 128.
      // 2. Tag 64-127 can be safely ignored.
      // 3. Tag 0-63 must be comprehended, therefore we cannot ignore.
      if (pConfig.options().warnMismatch()) {
        if ((pTag & 127) < 64) {
          warning(diag::warn_unknown_mandatory_attribute) << pTag
                                                          << pInput.name();
        } else {
          warning(diag::warn_unknown_attribute) << pTag << pInput.name();
        }
      }
      break;
    }
  }
  return true;
}

//===--------------------------------------------------------------------===//
// Helper Functions for postMerge()
//===--------------------------------------------------------------------===//

namespace {

/*
 * Helper function to encode value in Tag_also_compatible_with.
 *
 * @ref ARM [ABI-addenda], 2.3.7.3
 */
static void encode_secondary_compatibility_attribute(ELFAttributeValue& pValue,
                                                     int pArch) {
  if ((pArch < 0) || (pArch > ARMELFAttributeData::CPU_Arch_Max)) {
    pValue.setStringValue("");
  } else {
    char new_value[] = {
        ARMELFAttributeData::Tag_CPU_arch, static_cast<char>(pArch), 0};
    pValue.setStringValue(std::string(new_value, sizeof(new_value)));
  }
  return;
}

/*
 * Combine the main and secondary CPU arch value
 */
static int calculate_cpu_arch(int cpu_arch, int secondary_arch) {
  // short-circuit
  if ((secondary_arch < 0) ||
      ((cpu_arch + secondary_arch) != (ARMELFAttributeData::CPU_Arch_ARM_V4T +
                                       ARMELFAttributeData::CPU_Arch_ARM_V6_M)))
    return cpu_arch;

  if ((cpu_arch == ARMELFAttributeData::CPU_Arch_ARM_V4T) &&
      (secondary_arch == ARMELFAttributeData::CPU_Arch_ARM_V6_M))
    return ARMELFAttributeData::CPU_Arch_ARM_V4T_Plus_V6_M;
  else if ((cpu_arch == ARMELFAttributeData::CPU_Arch_ARM_V6_M) &&
           (secondary_arch == ARMELFAttributeData::CPU_Arch_ARM_V4T))
    return ARMELFAttributeData::CPU_Arch_ARM_V4T_Plus_V6_M;
  else
    return cpu_arch;
}

/*
 * Given a CPU arch X and a CPU arch Y in which Y is newer than X, the value in
 * cpu_compatibility_table[X][Y] is the CPU arch required to run ISA both from X
 * and Y. 0 in the table means unreachable and -1 means conflict architecture
 * profile.
 */
#define CPU(C)  ARMELFAttributeData::CPU_Arch_ARM_ ## C
static const int cpu_compatibility_table[][CPU(V4T_Plus_V6_M) + 1] = {
    /* old\new          ARM v6T2    ARM v6K   ARM v7   ARM v6-M   ARM v6S-M   ARM v7E-M    ARMv8, ARM v4t + v6-M     */  // NOLINT
    /* Pre v4     */ { CPU(V6T2),  CPU(V6K), CPU(V7),        -1,         -1,         -1,      -1,       -1           },  // NOLINT
    /* ARM v4     */ { CPU(V6T2),  CPU(V6K), CPU(V7),        -1,         -1,         -1,      -1,       -1           },  // NOLINT
    /* ARM v4T    */ { CPU(V6T2),  CPU(V6K), CPU(V7),  CPU(V6K),   CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V4T)           },  // NOLINT
    /* ARM v5T    */ { CPU(V6T2),  CPU(V6K), CPU(V7),  CPU(V6K),   CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V5T)           },  // NOLINT
    /* ARM v5TE   */ { CPU(V6T2),  CPU(V6K), CPU(V7),  CPU(V6K),   CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V5TE)          },  // NOLINT
    /* ARM v5TEJ  */ { CPU(V6T2),  CPU(V6K), CPU(V7),  CPU(V6K),   CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V5TEJ)         },  // NOLINT
    /* ARM v6     */ { CPU(V6T2),  CPU(V6K), CPU(V7),  CPU(V6K),   CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V6)            },  // NOLINT
    /* ARM v6KZ   */ {   CPU(V7), CPU(V6KZ), CPU(V7), CPU(V6KZ),  CPU(V6KZ), CPU(V7E_M), CPU(V8), CPU(V6KZ)          },  // NOLINT
    /* ARM v6T2   */ { CPU(V6T2),   CPU(V7), CPU(V7),   CPU(V7),    CPU(V7), CPU(V7E_M), CPU(V8), CPU(V6T2)          },  // NOLINT
    /* ARM v6K    */ {         0,  CPU(V6K), CPU(V7),  CPU(V6K),   CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V6K)           },  // NOLINT
    /* ARM v7     */ {         0,         0, CPU(V7),   CPU(V7),    CPU(V7), CPU(V7E_M), CPU(V8), CPU(V7)            },  // NOLINT
    /* ARM v6-M   */ {         0,         0,       0, CPU(V6_M), CPU(V6S_M), CPU(V7E_M), CPU(V8), CPU(V6_M)          },  // NOLINT
    /* ARM v6S-M  */ {         0,         0,       0,         0, CPU(V6S_M), CPU(V7E_M), CPU(V8), CPU(V6S_M)         },  // NOLINT
    /* ARM v7E-M  */ {         0,         0,       0,         0,          0, CPU(V7E_M), CPU(V8), CPU(V7E_M)         },  // NOLINT
    /* ARM v8     */ {         0,         0,       0,         0,          0,          0, CPU(V8), CPU(V8)            },  // NOLINT
    /* v4T + v6-M */ {         0,         0,       0,         0,          0,          0,       0, CPU(V4T_Plus_V6_M) }   // NOLINT
};

/*
 * Helper function to determine the merge of two different CPU arch.
 */
static int merge_cpu_arch(int out_cpu_arch, int in_cpu_arch) {
  if (out_cpu_arch > CPU(V4T_Plus_V6_M))
    return in_cpu_arch;

  int new_cpu_arch, old_cpu_arch;
  if (out_cpu_arch > in_cpu_arch) {
    new_cpu_arch = out_cpu_arch;
    old_cpu_arch = in_cpu_arch;
  } else {
    new_cpu_arch = in_cpu_arch;
    old_cpu_arch = out_cpu_arch;
  }

  // No need to check the compatibility since the CPU architectures before
  // V6KZ add features monotonically.
  if (new_cpu_arch <= CPU(V6KZ))
    return new_cpu_arch;

  return cpu_compatibility_table[old_cpu_arch][new_cpu_arch - CPU(V6T2)];
}
#undef CPU

/*
 * Generic CPU name is used when Tag_CPU_name is unable to guess during the
 * merge of Tag_CPU_arch.
 */
static const char* generic_cpu_name_table[] = {
    /* Pre v4    */ "Pre v4",
    /* Pre v4    */ "ARM v4",
    /* ARM v4T   */ "ARM v4T",
    /* ARM v5T   */ "ARM v5T",
    /* ARM v5TE  */ "ARM v5TE",
    /* ARM v5TEJ */ "ARM v5TEJ",
    /* ARM v6    */ "ARM v6",
    /* ARM v6KZ  */ "ARM v6KZ",
    /* ARM v6T2  */ "ARM v6T2",
    /* ARM v6K   */ "ARM v6K",
    /* ARM v7    */ "ARM v7",
    /* ARM v6-M  */ "ARM v6-M",
    /* ARM v6S-M */ "ARM v6S-M",
    /* ARM v7E-M */ "ARM v7E-M",
    /* ARM v8    */ "ARM v8",
};

static const char* get_generic_cpu_name(int cpu_arch) {
  assert(static_cast<size_t>(cpu_arch) <
         (sizeof(generic_cpu_name_table) / sizeof(generic_cpu_name_table[0])));
  return generic_cpu_name_table[cpu_arch];
}

/*
 * Helper functions & data used in the merge of two different FP arch.
 */
static const struct fp_config_data {
  int version;
  int regs;
} fp_configs[] = {
      {0, 0},
      {1, 16},
      {2, 16},
      {3, 32},
      {3, 16},
      {4, 32},
      {4, 16},
      {8, 32},
      {8, 16},
};

static const size_t num_fp_configs =
    sizeof(fp_configs) / sizeof(fp_config_data);

// Given h(x, y) = (x * (y >> 4) + (y >> 5))
//
// fp_config_hash_table[ h(0, 0)  =  0 ] = 0
// fp_config_hash_table[ h(1, 16) =  1 ] = 1
// fp_config_hash_table[ h(2, 16) =  2 ] = 2
// fp_config_hash_table[ h(3, 32) =  7 ] = 3
// fp_config_hash_table[ h(3, 16) =  3 ] = 4
// fp_config_hash_table[ h(4, 32) =  9 ] = 5
// fp_config_hash_table[ h(4, 16) =  4 ] = 6
// fp_config_hash_table[ h(8, 32) = 17 ] = 7
// fp_config_hash_table[ h(8, 16) =  8 ] = 8
//
// h(0, 0) = 0
static const uint8_t fp_config_hash_table[] = {
#define UND static_cast<uint8_t>(-1)
    /*  0 */ 0,
    /*  1 */ 1,
    /*  2 */ 2,
    /*  3 */ 4,
    /*  4 */ 6,
    /*  5 */ UND,
    /*  6 */ UND,
    /*  7 */ 3,
    /*  8 */ 8,
    /*  9 */ 5,
    /* 10 */ UND,
    /* 11 */ UND,
    /* 12 */ UND,
    /* 13 */ UND,
    /* 14 */ UND,
    /* 15 */ UND,
    /* 16 */ UND,
    /* 17 */ 7,
#undef UND
};

static int calculate_fp_config_hash(const struct fp_config_data& pConfig) {
  int x = pConfig.version;
  int y = pConfig.regs;
  return (x * (y >> 4) + (y >> 5));
}

static int get_fp_arch_of_config(const struct fp_config_data& pConfig) {
  int hash = calculate_fp_config_hash(pConfig);
  assert(static_cast<size_t>(hash) <
         llvm::array_lengthof(fp_config_hash_table));
  return fp_config_hash_table[hash];
}

static bool is_allowed_use_of_div(int cpu_arch,
                                  int cpu_arch_profile,
                                  int div_use) {
  // 0: The code was permitted to use SDIV and UDIV in the Thumb ISA on v7-R or
  //    v7-M.
  // 1: The code was not permitted to use SDIV and UDIV.
  // 2: The code was explicitly permitted to use SDIV and UDIV.
  switch (div_use) {
    case 0: {
      if ((cpu_arch == ARMELFAttributeData::CPU_Arch_ARM_V7) &&
          ((cpu_arch_profile == 'R') || (cpu_arch_profile == 'M'))) {
        return true;
      } else {
        return (cpu_arch >= ARMELFAttributeData::CPU_Arch_ARM_V7E_M);
      }
    }
    case 1: {
      return false;
    }
    case 2:
    // For future proofing
    default: { return true; }
  }
}

}  // anonymous namespace

//===--------------------------------------------------------------------===//
// End Helper Functions for postMerge()
//===--------------------------------------------------------------------===//

bool ARMELFAttributeData::postMerge(const LinkerConfig& pConfig,
                                    const Input& pInput) {
  // Process Tag_CPU_arch, Tag_CPU_name, Tag_CPU_raw_name, and
  // Tag_also_compatible_with.
  ELFAttributeValue& out_cpu_arch_attr = m_Attrs[Tag_CPU_arch];
  ELFAttributeValue& out_secondary_compatibility_attr =
      m_Attrs[Tag_also_compatible_with];

  if ((m_CurrentCPUArch < 0) && out_cpu_arch_attr.isInitialized()) {
    // Current input initializes the value of Tag_CPU_arch. Validate it.
    int out_cpu_arch = out_cpu_arch_attr.getIntValue();

    if (out_cpu_arch > CPU_Arch_Max) {
      error(diag::error_unknown_cpu_arch) << pInput.name();
      return false;
    }

    // Initialize m_CurrentCPUArch.
    int out_secondary_arch = -1;
    if (out_secondary_compatibility_attr.isInitialized())
      out_secondary_arch = decode_secondary_compatibility_attribute(
          out_secondary_compatibility_attr);

    m_CurrentCPUArch = calculate_cpu_arch(out_cpu_arch, out_secondary_arch);
  }

  if (m_CPUArch >= 0) {
    assert(out_cpu_arch_attr.isInitialized() && "CPU arch has never set!");
    assert(m_CurrentCPUArch >= 0);

    int in_cpu_arch = calculate_cpu_arch(m_CPUArch, m_SecondaryCPUArch);
    int result_cpu_arch = merge_cpu_arch(m_CurrentCPUArch, in_cpu_arch);

    if (result_cpu_arch < 0) {
      warning(diag::warn_mismatch_cpu_arch_profile) << in_cpu_arch
                                                    << pInput.name();
    } else {
      if (result_cpu_arch != m_CurrentCPUArch) {
        // Value of Tag_CPU_arch are going to changea.
        m_CurrentCPUArch = result_cpu_arch;

        // Write the result value to the output.
        if (result_cpu_arch == CPU_Arch_ARM_V4T_Plus_V6_M) {
          out_cpu_arch_attr.setIntValue(CPU_Arch_ARM_V4T);
          encode_secondary_compatibility_attribute(
              out_secondary_compatibility_attr, CPU_Arch_ARM_V6_M);
        } else {
          out_cpu_arch_attr.setIntValue(result_cpu_arch);
          encode_secondary_compatibility_attribute(
              out_secondary_compatibility_attr, -1);
        }

        ELFAttributeValue& out_cpu_name = m_Attrs[Tag_CPU_name];
        ELFAttributeValue& out_cpu_raw_name = m_Attrs[Tag_CPU_raw_name];

        if (m_CurrentCPUArch != in_cpu_arch) {
          // Unable to guess the Tag_CPU_name. Use the generic name.
          if (out_cpu_name.isInitialized()) {
            out_cpu_name.setStringValue(get_generic_cpu_name(m_CurrentCPUArch));
          }

          // Tag_CPU_raw_name becomes unknown. Set to default value to disable
          // it.
          out_cpu_raw_name.setStringValue("");
        } else {
          // Use the value of Tag_CPU_name and Tag_CPU_raw_name from the input.
          if (!m_CPUName.empty()) {
            ELFAttributeValue& out_cpu_name = m_Attrs[Tag_CPU_name];
            assert(out_cpu_name.isInitialized() && "CPU name has never set!");
            out_cpu_name.setStringValue(m_CPUName);
          }

          if (!m_CPURawName.empty()) {
            ELFAttributeValue& out_cpu_raw_name = m_Attrs[Tag_CPU_raw_name];
            assert(out_cpu_raw_name.isInitialized() &&
                   "CPU raw name has never set!");
            out_cpu_raw_name.setStringValue(m_CPURawName);
          }
        }
      }
    }
  }  // (m_CPUArch >= 0)

  // Process Tag_ABI_VFP_args.
  if (m_VFPArgs >= 0) {
    ELFAttributeValue& out_attr = m_Attrs[Tag_ABI_VFP_args];
    ELFAttributeValue& out_float_number_model_attr =
        m_Attrs[Tag_ABI_FP_number_model];

    assert(out_attr.isInitialized() && "VFP args has never set!");

    // If the output is not permitted to use floating number, this attribute
    // is ignored (migrate the value from input directly.)
    if (out_float_number_model_attr.isUninitialized() ||
        (out_float_number_model_attr.getIntValue() == 0)) {
      // Inherit requirement from input.
      out_attr.setIntValue(m_VFPArgs);
    } else {
      if (pConfig.options().warnMismatch())
        warning(diag::warn_mismatch_vfp_args) << pInput.name();
    }
  }

  // Process Tag_FP_arch.
  ELFAttributeValue& out_fp_arch_attr = m_Attrs[Tag_FP_arch];
  if (m_FPArch >= 0) {
    assert(out_fp_arch_attr.isInitialized() && "FP arch has never set!");

    // Tag_FP_arch
    //  0: instructions requiring FP hardware are not permitted
    //  1: VFP1
    //  2: VFP2
    //  3: VFP3 D32
    //  4: VFP3 D16
    //  5: VFP4 D32
    //  6: VFP4 D16
    //  7: ARM v8-A D32
    //  8: ARM v8-A D16
    if (out_fp_arch_attr.getIntValue() == 0) {
      // Output has no constraints on FP hardware. Copy the requirement from
      // input.
      out_fp_arch_attr.setIntValue(m_FPArch);
    } else if (m_FPArch == 0) {
      // Input has no constraints on FP hardware. Do nothing.
    } else {
      // If here, both output and input contain non-zero value of Tag_FP_arch.

      // Version greater than num_fp_configs is not defined. Choose the greater
      // one for future-proofing.
      if (static_cast<unsigned>(m_FPArch) > num_fp_configs) {
        if (static_cast<unsigned>(m_FPArch) > out_fp_arch_attr.getIntValue()) {
          out_fp_arch_attr.setIntValue(m_FPArch);
        }
      } else {
        if (out_fp_arch_attr.getIntValue() < num_fp_configs) {
          const struct fp_config_data& input_fp_config = fp_configs[m_FPArch];

          const struct fp_config_data& output_fp_config =
              fp_configs[out_fp_arch_attr.getIntValue()];

          const struct fp_config_data result_fp_config = {
              /*version*/ ((output_fp_config.version > input_fp_config.version)
                               ? output_fp_config.version
                               : input_fp_config.version),
              /* regs */ ((output_fp_config.regs > input_fp_config.regs)
                              ? output_fp_config.regs
                              : input_fp_config.regs),
          };
          // Find the attribute value corresponding the result_fp_config
          out_fp_arch_attr.setIntValue(get_fp_arch_of_config(result_fp_config));
        }
      }
    }
  }  // (m_FPArch >= 0)

  // Process Tag_ABI_HardFP_use.
  ELFAttributeValue& out_hardfp_use_attr = m_Attrs[Tag_ABI_HardFP_use];

  if (!m_HardFPUseInitialized && out_hardfp_use_attr.isInitialized()) {
    m_HardFPUse = out_hardfp_use_attr.getIntValue();
    m_HardFPUseInitialized = true;
  }

  if (m_HardFPUse >= 0) {
    // Tag_ABI_HardFP_use depends on the meaning of Tag_FP_arch when it's 0.
    assert(out_hardfp_use_attr.isInitialized() && "HardFP use has never set!");

    if (out_fp_arch_attr.isUninitialized() ||
        (out_fp_arch_attr.getIntValue() == 0)) {
      // Has no constraints on FP hardware.
      out_hardfp_use_attr.setIntValue(m_HardFPUse);
    } else {
      // Both output and input contain non-zero value of Tag_FP_arch and we have
      // different Tag_ABI_HaedFP_Use settings other than 0.
      if ((out_fp_arch_attr.getIntValue() > 0) && (m_HardFPUse > 0))
        // Promote to 3 (The user permitted this entity to use both SP and DP
        // VFP instruction.)
        out_hardfp_use_attr.setIntValue(3);
    }
  }

  // Move the value of Tag_MPextension_use_legacy to Tag_MPextension_use.
  ELFAttributeValue& out_mpextension_use_legacy =
      m_Attrs[Tag_MPextension_use_legacy];

  ELFAttributeValue& out_mpextension_use = m_Attrs[Tag_MPextension_use];

  // If Tag_MPextension_use_legacy has value, it must be introduced by current
  // input since it is reset every time after the merge completed.
  if (out_mpextension_use_legacy.isInitialized()) {
    if (out_mpextension_use.isInitialized()) {
      if (m_MPextensionUse < 0) {
        // The value of Tag_MPextension_use is introduced by the current input.
        // Check whether it is consistent with the one set in legacy.
        m_MPextensionUse = out_mpextension_use.getIntValue();
      } else {
        // Current input introduces value of Tag_MPextension_use in
        // m_MPextensionUse.
      }

      // Check the consistency between m_MPextensionUse and the value of
      // Tag_MPextension_use_legacy.
      if (static_cast<unsigned>(m_MPextensionUse) !=
          out_mpextension_use_legacy.getIntValue()) {
        error(diag::error_mismatch_mpextension_use) << pInput.name();
        return false;
      }
    } else {
      if (m_MPextensionUse < 0) {
        // Tag_MPextension_use is not set. Initialize it and move the value.
        out_mpextension_use.setType(ELFAttributeValue::Int);
        out_mpextension_use.setIntValue(out_mpextension_use.getIntValue());
      } else {
        // Unreachable case since the value to unitialized attribute is directly
        // assigned in ELFAttribute::Subsection::merge().
        assert(false && "Tag_MPextension_use is uninitialized but have value?");
      }
    }

    // Reset the attribute to uninitialized so it won't be included in the
    // output.
    out_mpextension_use_legacy.setType(ELFAttributeValue::Uninitialized);
  }

  // Process Tag_MPextension_use.
  if (m_MPextensionUse > 0) {
    assert(out_mpextension_use.isInitialized());

    if (static_cast<unsigned>(m_MPextensionUse) >
        out_mpextension_use.getIntValue()) {
      out_mpextension_use.setIntValue(m_MPextensionUse);
    }
  }

  // Process Tag_DIV_use.
  ELFAttributeValue& out_div_use_attr = m_Attrs[Tag_DIV_use];

  if (!m_DIVUseInitialized && out_div_use_attr.isInitialized()) {
    // Perform the merge by reverting value of Tag_DIV_use and setup m_DIVUse.
    m_DIVUse = out_div_use_attr.getIntValue();
    out_div_use_attr.setIntValue(0);
    m_DIVUseInitialized = true;
  }

  if (m_DIVUse >= 0) {
    assert(out_div_use_attr.isInitialized());

    const ELFAttributeValue& out_cpu_arch_profile_attr =
        m_Attrs[Tag_CPU_arch_profile];

    int out_cpu_arch_profile = Arch_Profile_None;
    if (out_cpu_arch_profile_attr.isInitialized()) {
      out_cpu_arch_profile = out_cpu_arch_profile_attr.getIntValue();
    }

    if (m_DIVUse == 1) {
      // Input (=1) was not permitted to use SDIV and UDIV. See whether current
      // output was explicitly permitted the use.
      if (!is_allowed_use_of_div(m_CurrentCPUArch,
                                 out_cpu_arch_profile,
                                 out_div_use_attr.getIntValue())) {
        out_div_use_attr.setIntValue(1);
      }
    } else {
      if (out_div_use_attr.getIntValue() != 1) {
        // Output does not explicitly forbid the use of SDIV/UDIV. See whether
        // the input attribute can allow it under current CPU architecture
        // profile.
        if (is_allowed_use_of_div(
                m_CurrentCPUArch, out_cpu_arch_profile, m_DIVUse)) {
          out_div_use_attr.setIntValue(m_DIVUse);
        }
      }
    }
  }

  return true;
}

size_t ARMELFAttributeData::sizeOutput() const {
  size_t result = 0;

  // Size contributed by known attributes
  for (unsigned i = 0; i <= Tag_Max; ++i) {
    TagType tag = static_cast<TagType>(i);
    const ELFAttributeValue& value = m_Attrs[tag];

    if (value.shouldEmit()) {
      result += leb128::size(static_cast<uint32_t>(tag));
      result += value.getSize();
    }
  }

  // Size contributed by unknown attributes
  for (UnknownAttrsMap::const_iterator unknown_attr_it = m_UnknownAttrs.begin(),
                                       unknown_attr_end = m_UnknownAttrs.end();
       unknown_attr_it != unknown_attr_end;
       ++unknown_attr_it) {
    TagType tag = unknown_attr_it->first;
    const ELFAttributeValue& value = unknown_attr_it->second;

    if (value.shouldEmit()) {
      result += leb128::size(static_cast<uint32_t>(tag));
      result += value.getSize();
    }
  }

  return result;
}

size_t ARMELFAttributeData::emit(char* pBuf) const {
  char* buffer = pBuf;

  // Tag_conformance "should be emitted first in a file-scope sub-subsection of
  // the first public subsection of the attribute section."
  //
  // See ARM [ABI-addenda], 2.3.7.4 Conformance tag
  const ELFAttributeValue& attr_conformance = m_Attrs[Tag_conformance];

  if (attr_conformance.shouldEmit()) {
    if (!ELFAttributeData::WriteAttribute(
            Tag_conformance, attr_conformance, buffer)) {
      return 0;
    }
  }

  // Tag_nodefaults "should be emitted before any other tag in an attribute
  // subsection other that the conformance tag"
  //
  // See ARM [ABI-addenda], 2.3.7.5 No defaults tag
  const ELFAttributeValue& attr_nodefaults = m_Attrs[Tag_nodefaults];

  if (attr_nodefaults.shouldEmit()) {
    if (!ELFAttributeData::WriteAttribute(
            Tag_nodefaults, attr_nodefaults, buffer)) {
      return 0;
    }
  }

  // Tag_conformance (=67)
  // Tag_nodefaults (=64)
  for (unsigned i = 0; i < Tag_nodefaults; ++i) {
    TagType tag = static_cast<TagType>(i);
    const ELFAttributeValue& value = m_Attrs[tag];

    if (value.shouldEmit() &&
        !ELFAttributeData::WriteAttribute(tag, value, buffer)) {
      return 0;
    }
  }

  for (unsigned i = (Tag_nodefaults + 1); i <= Tag_Max; ++i) {
    TagType tag = static_cast<TagType>(i);
    const ELFAttributeValue& value = m_Attrs[tag];

    if (value.shouldEmit() && (i != Tag_conformance) &&
        !ELFAttributeData::WriteAttribute(tag, value, buffer)) {
      return 0;
    }
  }

  for (UnknownAttrsMap::const_iterator unknown_attr_it = m_UnknownAttrs.begin(),
                                       unknown_attr_end = m_UnknownAttrs.end();
       unknown_attr_it != unknown_attr_end;
       ++unknown_attr_it) {
    TagType tag = unknown_attr_it->first;
    const ELFAttributeValue& value = unknown_attr_it->second;

    if (value.shouldEmit() &&
        !ELFAttributeData::WriteAttribute(tag, value, buffer)) {
      return 0;
    }
  }

  return (buffer - pBuf);
}

bool ARMELFAttributeData::usingThumb() const {
  int arch = m_Attrs[Tag_CPU_arch].getIntValue();
  if ((arch == CPU_Arch_ARM_V6_M) || (arch == CPU_Arch_ARM_V6S_M))
    return true;
  if ((arch != CPU_Arch_ARM_V7) && (arch != CPU_Arch_ARM_V7E_M))
    return false;

  arch = m_Attrs[Tag_CPU_arch_profile].getIntValue();
  return arch == Arch_Profile_Microcontroller;
}

bool ARMELFAttributeData::usingThumb2() const {
  int arch = m_Attrs[Tag_CPU_arch].getIntValue();
  return (arch == CPU_Arch_ARM_V6T2) || (arch == CPU_Arch_ARM_V7);
}

}  // namespace mcld