//===-- ARMBaseInfo.h - Top level definitions for ARM ---*- 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
//
//===----------------------------------------------------------------------===//
//
// This file contains small standalone helper functions and enum definitions for
// the ARM target useful for the compiler back-end and the MC libraries.
// As such, it deliberately does not include references to LLVM core
// code gen types, passes, etc..
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_ARM_UTILS_ARMBASEINFO_H
#define LLVM_LIB_TARGET_ARM_UTILS_ARMBASEINFO_H

#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/MC/SubtargetFeature.h"
#include "MCTargetDesc/ARMMCTargetDesc.h"

namespace llvm {

// Enums corresponding to ARM condition codes
namespace ARMCC {
// The CondCodes constants map directly to the 4-bit encoding of the
// condition field for predicated instructions.
enum CondCodes { // Meaning (integer)          Meaning (floating-point)
  EQ,            // Equal                      Equal
  NE,            // Not equal                  Not equal, or unordered
  HS,            // Carry set                  >, ==, or unordered
  LO,            // Carry clear                Less than
  MI,            // Minus, negative            Less than
  PL,            // Plus, positive or zero     >, ==, or unordered
  VS,            // Overflow                   Unordered
  VC,            // No overflow                Not unordered
  HI,            // Unsigned higher            Greater than, or unordered
  LS,            // Unsigned lower or same     Less than or equal
  GE,            // Greater than or equal      Greater than or equal
  LT,            // Less than                  Less than, or unordered
  GT,            // Greater than               Greater than
  LE,            // Less than or equal         <, ==, or unordered
  AL             // Always (unconditional)     Always (unconditional)
};

inline static CondCodes getOppositeCondition(CondCodes CC) {
  switch (CC) {
  default: llvm_unreachable("Unknown condition code");
  case EQ: return NE;
  case NE: return EQ;
  case HS: return LO;
  case LO: return HS;
  case MI: return PL;
  case PL: return MI;
  case VS: return VC;
  case VC: return VS;
  case HI: return LS;
  case LS: return HI;
  case GE: return LT;
  case LT: return GE;
  case GT: return LE;
  case LE: return GT;
  }
}

/// getSwappedCondition - assume the flags are set by MI(a,b), return
/// the condition code if we modify the instructions such that flags are
/// set by MI(b,a).
inline static ARMCC::CondCodes getSwappedCondition(ARMCC::CondCodes CC) {
  switch (CC) {
  default: return ARMCC::AL;
  case ARMCC::EQ: return ARMCC::EQ;
  case ARMCC::NE: return ARMCC::NE;
  case ARMCC::HS: return ARMCC::LS;
  case ARMCC::LO: return ARMCC::HI;
  case ARMCC::HI: return ARMCC::LO;
  case ARMCC::LS: return ARMCC::HS;
  case ARMCC::GE: return ARMCC::LE;
  case ARMCC::LT: return ARMCC::GT;
  case ARMCC::GT: return ARMCC::LT;
  case ARMCC::LE: return ARMCC::GE;
  }
}
} // end namespace ARMCC

namespace ARMVCC {
  enum VPTCodes {
    None = 0,
    Then,
    Else
  };
} // namespace ARMVCC

namespace ARM {
  /// Mask values for IT and VPT Blocks, to be used by MCOperands.
  /// Note that this is different from the "real" encoding used by the
  /// instructions. In this encoding, the lowest set bit indicates the end of
  /// the encoding, and above that, "1" indicates an else, while "0" indicates
  /// a then.
  ///   Tx = x100
  ///   Txy = xy10
  ///   Txyz = xyz1
  enum class PredBlockMask {
    T = 0b1000,
    TT = 0b0100,
    TE = 0b1100,
    TTT = 0b0010,
    TTE = 0b0110,
    TEE = 0b1110,
    TET = 0b1010,
    TTTT = 0b0001,
    TTTE = 0b0011,
    TTEE = 0b0111,
    TTET = 0b0101,
    TEEE = 0b1111,
    TEET = 0b1101,
    TETT = 0b1001,
    TETE = 0b1011
  };
} // namespace ARM

// Expands a PredBlockMask by adding an E or a T at the end, depending on Kind.
// e.g ExpandPredBlockMask(T, Then) = TT, ExpandPredBlockMask(TT, Else) = TTE,
// and so on.
ARM::PredBlockMask expandPredBlockMask(ARM::PredBlockMask BlockMask,
                                       ARMVCC::VPTCodes Kind);

inline static const char *ARMVPTPredToString(ARMVCC::VPTCodes CC) {
  switch (CC) {
  case ARMVCC::None:  return "none";
  case ARMVCC::Then:  return "t";
  case ARMVCC::Else:  return "e";
  }
  llvm_unreachable("Unknown VPT code");
}

inline static unsigned ARMVectorCondCodeFromString(StringRef CC) {
  return StringSwitch<unsigned>(CC.lower())
    .Case("t", ARMVCC::Then)
    .Case("e", ARMVCC::Else)
    .Default(~0U);
}

inline static const char *ARMCondCodeToString(ARMCC::CondCodes CC) {
  switch (CC) {
  case ARMCC::EQ:  return "eq";
  case ARMCC::NE:  return "ne";
  case ARMCC::HS:  return "hs";
  case ARMCC::LO:  return "lo";
  case ARMCC::MI:  return "mi";
  case ARMCC::PL:  return "pl";
  case ARMCC::VS:  return "vs";
  case ARMCC::VC:  return "vc";
  case ARMCC::HI:  return "hi";
  case ARMCC::LS:  return "ls";
  case ARMCC::GE:  return "ge";
  case ARMCC::LT:  return "lt";
  case ARMCC::GT:  return "gt";
  case ARMCC::LE:  return "le";
  case ARMCC::AL:  return "al";
  }
  llvm_unreachable("Unknown condition code");
}

inline static unsigned ARMCondCodeFromString(StringRef CC) {
  return StringSwitch<unsigned>(CC.lower())
    .Case("eq", ARMCC::EQ)
    .Case("ne", ARMCC::NE)
    .Case("hs", ARMCC::HS)
    .Case("cs", ARMCC::HS)
    .Case("lo", ARMCC::LO)
    .Case("cc", ARMCC::LO)
    .Case("mi", ARMCC::MI)
    .Case("pl", ARMCC::PL)
    .Case("vs", ARMCC::VS)
    .Case("vc", ARMCC::VC)
    .Case("hi", ARMCC::HI)
    .Case("ls", ARMCC::LS)
    .Case("ge", ARMCC::GE)
    .Case("lt", ARMCC::LT)
    .Case("gt", ARMCC::GT)
    .Case("le", ARMCC::LE)
    .Case("al", ARMCC::AL)
    .Default(~0U);
}

// System Registers
namespace ARMSysReg {
  struct MClassSysReg {
    const char *Name;
    uint16_t M1Encoding12;
    uint16_t M2M3Encoding8;
    uint16_t Encoding;
    FeatureBitset FeaturesRequired;

    // return true if FeaturesRequired are all present in ActiveFeatures
    bool hasRequiredFeatures(FeatureBitset ActiveFeatures) const {
      return (FeaturesRequired & ActiveFeatures) == FeaturesRequired;
    }

    // returns true if TestFeatures are all present in FeaturesRequired
    bool isInRequiredFeatures(FeatureBitset TestFeatures) const {
      return (FeaturesRequired & TestFeatures) == TestFeatures;
    }
  };

  #define GET_MCLASSSYSREG_DECL
  #include "ARMGenSystemRegister.inc"

  // lookup system register using 12-bit SYSm value.
  // Note: the search is uniqued using M1 mask
  const MClassSysReg *lookupMClassSysRegBy12bitSYSmValue(unsigned SYSm);

  // returns APSR with _<bits> qualifier.
  // Note: ARMv7-M deprecates using MSR APSR without a _<bits> qualifier
  const MClassSysReg *lookupMClassSysRegAPSRNonDeprecated(unsigned SYSm);

  // lookup system registers using 8-bit SYSm value
  const MClassSysReg *lookupMClassSysRegBy8bitSYSmValue(unsigned SYSm);

} // end namespace ARMSysReg

// Banked Registers
namespace ARMBankedReg {
  struct BankedReg {
    const char *Name;
    uint16_t Encoding;
  };
  #define GET_BANKEDREG_DECL
  #include "ARMGenSystemRegister.inc"
} // end namespace ARMBankedReg

} // end namespace llvm

#endif // LLVM_LIB_TARGET_ARM_UTILS_ARMBASEINFO_H
