//===--- ARM.h - Declare ARM target feature support -------------*- 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 declares ARM TargetInfo objects.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_ARM_H
#define LLVM_CLANG_LIB_BASIC_TARGETS_ARM_H

#include "OSTargets.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/ARMTargetParser.h"
#include "llvm/TargetParser/ARMTargetParserCommon.h"
#include "llvm/TargetParser/Triple.h"

namespace clang {
namespace targets {

class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public TargetInfo {
  // Possible FPU choices.
  enum FPUMode {
    VFP2FPU = (1 << 0),
    VFP3FPU = (1 << 1),
    VFP4FPU = (1 << 2),
    NeonFPU = (1 << 3),
    FPARMV8 = (1 << 4)
  };

  enum MVEMode {
      MVE_INT = (1 << 0),
      MVE_FP  = (1 << 1)
  };

  // Possible HWDiv features.
  enum HWDivMode { HWDivThumb = (1 << 0), HWDivARM = (1 << 1) };

  static bool FPUModeIsVFP(FPUMode Mode) {
    return Mode & (VFP2FPU | VFP3FPU | VFP4FPU | NeonFPU | FPARMV8);
  }

  static const TargetInfo::GCCRegAlias GCCRegAliases[];
  static const char *const GCCRegNames[];

  std::string ABI, CPU;

  StringRef CPUProfile;
  StringRef CPUAttr;

  enum { FP_Default, FP_VFP, FP_Neon } FPMath;

  llvm::ARM::ISAKind ArchISA;
  llvm::ARM::ArchKind ArchKind = llvm::ARM::ArchKind::ARMV4T;
  llvm::ARM::ProfileKind ArchProfile;
  unsigned ArchVersion;

  LLVM_PREFERRED_TYPE(FPUMode)
  unsigned FPU : 5;
  LLVM_PREFERRED_TYPE(MVEMode)
  unsigned MVE : 2;

  LLVM_PREFERRED_TYPE(bool)
  unsigned IsAAPCS : 1;
  LLVM_PREFERRED_TYPE(HWDivMode)
  unsigned HWDiv : 2;

  // Initialized via features.
  LLVM_PREFERRED_TYPE(bool)
  unsigned SoftFloat : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned SoftFloatABI : 1;

  LLVM_PREFERRED_TYPE(bool)
  unsigned CRC : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned Crypto : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned SHA2 : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned AES : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned DSP : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned DotProd : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned HasMatMul : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned FPRegsDisabled : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned HasPAC : 1;
  LLVM_PREFERRED_TYPE(bool)
  unsigned HasBTI : 1;

  uint32_t LDREX;

  // ACLE 6.5.1 Hardware floating point
  enum {
    HW_FP_HP = (1 << 1), /// half (16-bit)
    HW_FP_SP = (1 << 2), /// single (32-bit)
    HW_FP_DP = (1 << 3), /// double (64-bit)
  };
  uint32_t HW_FP;

  enum {
    /// __arm_cdp __arm_ldc, __arm_ldcl, __arm_stc,
    /// __arm_stcl, __arm_mcr and __arm_mrc
    FEATURE_COPROC_B1 = (1 << 0),
    /// __arm_cdp2, __arm_ldc2, __arm_stc2, __arm_ldc2l,
    /// __arm_stc2l, __arm_mcr2 and __arm_mrc2
    FEATURE_COPROC_B2 = (1 << 1),
    /// __arm_mcrr, __arm_mrrc
    FEATURE_COPROC_B3 = (1 << 2),
    /// __arm_mcrr2,  __arm_mrrc2
    FEATURE_COPROC_B4 = (1 << 3),
  };

  void setABIAAPCS();
  void setABIAPCS(bool IsAAPCS16);

  void setArchInfo();
  void setArchInfo(llvm::ARM::ArchKind Kind);

  void setAtomic();

  bool isThumb() const;
  bool supportsThumb() const;
  bool supportsThumb2() const;
  bool hasMVE() const;
  bool hasMVEFloat() const;
  bool hasCDE() const;

  StringRef getCPUAttr() const;
  StringRef getCPUProfile() const;

public:
  ARMTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);

  StringRef getABI() const override;
  bool setABI(const std::string &Name) override;

  bool isBranchProtectionSupportedArch(StringRef Arch) const override;
  bool validateBranchProtection(StringRef Spec, StringRef Arch,
                                BranchProtectionInfo &BPI,
                                const LangOptions &LO,
                                StringRef &Err) const override;

  // FIXME: This should be based on Arch attributes, not CPU names.
  bool
  initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags,
                 StringRef CPU,
                 const std::vector<std::string> &FeaturesVec) const override;

  bool isValidFeatureName(StringRef Feature) const override {
    // We pass soft-float-abi in as a -target-feature, but the backend figures
    // this out through other means.
    return Feature != "soft-float-abi";
  }

  bool handleTargetFeatures(std::vector<std::string> &Features,
                            DiagnosticsEngine &Diags) override;

  bool hasFeature(StringRef Feature) const override;

  bool hasBFloat16Type() const override;

  bool isValidCPUName(StringRef Name) const override;
  void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;

  bool setCPU(const std::string &Name) override;

  bool setFPMath(StringRef Name) override;

  bool useFP16ConversionIntrinsics() const override {
    return false;
  }

  void getTargetDefinesARMV81A(const LangOptions &Opts,
                               MacroBuilder &Builder) const;
  void getTargetDefinesARMV82A(const LangOptions &Opts,
                               MacroBuilder &Builder) const;
  void getTargetDefinesARMV83A(const LangOptions &Opts,
                                 MacroBuilder &Builder) const;
  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;

  llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override;

  bool isCLZForZeroUndef() const override;
  BuiltinVaListKind getBuiltinVaListKind() const override;

  ArrayRef<const char *> getGCCRegNames() const override;
  ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override;
  bool validateAsmConstraint(const char *&Name,
                             TargetInfo::ConstraintInfo &Info) const override;
  std::string convertConstraint(const char *&Constraint) const override;
  bool
  validateConstraintModifier(StringRef Constraint, char Modifier, unsigned Size,
                             std::string &SuggestedModifier) const override;
  std::string_view getClobbers() const override;

  StringRef getConstraintRegister(StringRef Constraint,
                                  StringRef Expression) const override {
    return Expression;
  }

  CallingConvCheckResult checkCallingConvention(CallingConv CC) const override;

  int getEHDataRegisterNumber(unsigned RegNo) const override;

  bool hasSjLjLowering() const override;

  bool hasBitIntType() const override { return true; }

  unsigned getARMLDREXMask() const override { return LDREX; }

  const char *getBFloat16Mangling() const override { return "u6__bf16"; };

  std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
    return std::make_pair(64, 64);
  }
};

class LLVM_LIBRARY_VISIBILITY ARMleTargetInfo : public ARMTargetInfo {
public:
  ARMleTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;
};

class LLVM_LIBRARY_VISIBILITY ARMbeTargetInfo : public ARMTargetInfo {
public:
  ARMbeTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;
};

class LLVM_LIBRARY_VISIBILITY WindowsARMTargetInfo
    : public WindowsTargetInfo<ARMleTargetInfo> {
  const llvm::Triple Triple;

public:
  WindowsARMTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);

  void getVisualStudioDefines(const LangOptions &Opts,
                              MacroBuilder &Builder) const;

  BuiltinVaListKind getBuiltinVaListKind() const override;

  CallingConvCheckResult checkCallingConvention(CallingConv CC) const override;
};

// Windows ARM + Itanium C++ ABI Target
class LLVM_LIBRARY_VISIBILITY ItaniumWindowsARMleTargetInfo
    : public WindowsARMTargetInfo {
public:
  ItaniumWindowsARMleTargetInfo(const llvm::Triple &Triple,
                                const TargetOptions &Opts);

  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;
};

// Windows ARM, MS (C++) ABI
class LLVM_LIBRARY_VISIBILITY MicrosoftARMleTargetInfo
    : public WindowsARMTargetInfo {
public:
  MicrosoftARMleTargetInfo(const llvm::Triple &Triple,
                           const TargetOptions &Opts);

  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;
};

// ARM MinGW target
class LLVM_LIBRARY_VISIBILITY MinGWARMTargetInfo : public WindowsARMTargetInfo {
public:
  MinGWARMTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);

  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;
};

// ARM Cygwin target
class LLVM_LIBRARY_VISIBILITY CygwinARMTargetInfo : public ARMleTargetInfo {
public:
  CygwinARMTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);

  void getTargetDefines(const LangOptions &Opts,
                        MacroBuilder &Builder) const override;
};

class LLVM_LIBRARY_VISIBILITY AppleMachOARMTargetInfo
    : public AppleMachOTargetInfo<ARMleTargetInfo> {
protected:
  void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
                    MacroBuilder &Builder) const override;

public:
  AppleMachOARMTargetInfo(const llvm::Triple &Triple,
                          const TargetOptions &Opts);
};

class LLVM_LIBRARY_VISIBILITY DarwinARMTargetInfo
    : public DarwinTargetInfo<ARMleTargetInfo> {
protected:
  void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
                    MacroBuilder &Builder) const override;

public:
  DarwinARMTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts);
};

} // namespace targets
} // namespace clang

#endif // LLVM_CLANG_LIB_BASIC_TARGETS_ARM_H
