// AsmJit - Machine code generation for C++
//
//  * Official AsmJit Home Page: https://asmjit.com
//  * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

#ifndef ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
#define ASMJIT_CORE_ARCHTRAITS_H_INCLUDED

#include "../core/environment.h"
#include "../core/operand.h"
#include "../core/type.h"

ASMJIT_BEGIN_NAMESPACE

//! \addtogroup asmjit_core
//! \{

// ============================================================================
// [asmjit::ArchTraits]
// ============================================================================

//! Architecture traits used by Function API and Compiler's register allocator.
struct ArchTraits {
  //! ISA features for each register group.
  enum IsaFeatures : uint32_t {
    //! ISA features a register swap by using a single instruction.
    kIsaFeatureSwap = 0x01u,
    //! ISA features a push/pop like instruction for this register group.
    kIsaFeaturePushPop = 0x02u,
  };

  //! Stack pointer register id.
  uint8_t _spRegId;
  //! Frame pointer register id.
  uint8_t _fpRegId;
  //! Link register id.
  uint8_t _linkRegId;
  //! Instruction pointer (or program counter) register id, if accessible.
  uint8_t _ipRegId;

  // Reserved.
  uint8_t _reserved[3];
  //! Hardware stack alignment requirement.
  uint8_t _hwStackAlignment;
  //! Minimum addressable offset on stack guaranteed for all instructions.
  uint32_t _minStackOffset;
  //! Maximum addressable offset on stack depending on specific instruction.
  uint32_t _maxStackOffset;

  //! Flags for each virtual register group (always covers GP and Vec groups).
  uint8_t _isaFlags[BaseReg::kGroupVirt];

  //! Maps register type into a signature, that provides group, size and can
  //! be used to construct register operands.
  RegInfo _regInfo[BaseReg::kTypeMax + 1];
  //! Maps a register to type-id, see \ref Type::Id.
  uint8_t _regTypeToTypeId[BaseReg::kTypeMax + 1];
  //! Maps base TypeId values (from TypeId::_kIdBaseStart) to register types, see \ref Type::Id.
  uint8_t _typeIdToRegType[32];

  //! Resets all members to zeros.
  inline void reset() noexcept { memset(this, 0, sizeof(*this)); }

  //! \name Accessors
  //! \{

  //! Returns stack pointer register id.
  inline constexpr uint32_t spRegId() const noexcept { return _spRegId; }
  //! Returns stack frame register id.
  inline constexpr uint32_t fpRegId() const noexcept { return _fpRegId; }
  //! Returns link register id, if the architecture provides it.
  inline constexpr uint32_t linkRegId() const noexcept { return _linkRegId; }
  //! Returns instruction pointer register id, if the architecture provides it.
  inline constexpr uint32_t ipRegId() const noexcept { return _ipRegId; }

  //! Returns a hardware stack alignment requirement.
  //!
  //! \note This is a hardware constraint. Architectures that don't constrain
  //! it would return the lowest alignment (1), however, some architectures may
  //! constrain the alignment, for example AArch64 requires 16-byte alignment.
  inline constexpr uint32_t hwStackAlignment() const noexcept { return _hwStackAlignment; }

  //! Tests whether the architecture provides link register, which is used across
  //! function calls. If the link register is not provided then a function call
  //! pushes the return address on stack (X86/X64).
  inline constexpr bool hasLinkReg() const noexcept { return _linkRegId != BaseReg::kIdBad; }

  //! Returns minimum addressable offset on stack guaranteed for all instructions.
  inline constexpr uint32_t minStackOffset() const noexcept { return _minStackOffset; }
  //! Returns maximum addressable offset on stack depending on specific instruction.
  inline constexpr uint32_t maxStackOffset() const noexcept { return _maxStackOffset; }

  //! Returns ISA flags of the given register `group`.
  inline constexpr uint32_t isaFlags(uint32_t group) const noexcept { return _isaFlags[group]; }
  //! Tests whether the given register `group` has the given `flag` set.
  inline constexpr bool hasIsaFlag(uint32_t group, uint32_t flag) const noexcept { return (_isaFlags[group] & flag) != 0; }
  //! Tests whether the ISA provides register swap instruction for the given register `group`.
  inline constexpr bool hasSwap(uint32_t group) const noexcept { return hasIsaFlag(group, kIsaFeatureSwap); }
  //! Tests whether the ISA provides push/pop instructions for the given register `group`.
  inline constexpr bool hasPushPop(uint32_t group) const noexcept { return hasIsaFlag(group, kIsaFeaturePushPop); }

  inline uint32_t hasRegType(uint32_t rType) const noexcept {
    return rType <= BaseReg::kTypeMax && _regInfo[rType].signature() != 0;
  }

  inline uint32_t regTypeToSignature(uint32_t rType) const noexcept {
    ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
    return _regInfo[rType].signature();
  }

  inline uint32_t regTypeToGroup(uint32_t rType) const noexcept {
    ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
    return _regInfo[rType].group();
  }

  inline uint32_t regTypeToSize(uint32_t rType) const noexcept {
    ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
    return _regInfo[rType].size();
  }

  inline uint32_t regTypeToTypeId(uint32_t rType) const noexcept {
    ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
    return _regTypeToTypeId[rType];
  }

  //! \}

  //! \name Statics
  //! \{

  //! Returns a const reference to `ArchTraits` for the given architecture `arch`.
  static inline const ArchTraits& byArch(uint32_t arch) noexcept;

  //! \}
};

ASMJIT_VARAPI const ArchTraits _archTraits[Environment::kArchCount];

inline const ArchTraits& ArchTraits::byArch(uint32_t arch) noexcept { return _archTraits[arch & ~Environment::kArchBigEndianMask]; }

// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================

//! Architecture utilities.
namespace ArchUtils {

ASMJIT_API Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfo) noexcept;

} // {ArchUtils}

//! \}

ASMJIT_END_NAMESPACE

#endif // ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
