// This file is part of AsmJit project // // See asmjit.h or LICENSE.md for license and copyright information // SPDX-License-Identifier: Zlib #ifndef ASMJIT_CORE_ARCHTRAITS_H_INCLUDED #define ASMJIT_CORE_ARCHTRAITS_H_INCLUDED #include "../core/operand.h" #include "../core/support.h" #include "../core/type.h" ASMJIT_BEGIN_NAMESPACE //! \addtogroup asmjit_core //! \{ //! Instruction set architecture (ISA). enum class Arch : uint8_t { //! Unknown or uninitialized ISA. kUnknown = 0, //! 32-bit X86 ISA. kX86 = 1, //! 64-bit X86 ISA also known as X64, X86_64, and AMD64. kX64 = 2, //! 32-bit RISC-V ISA. kRISCV32 = 3, //! 64-bit RISC-V ISA. kRISCV64 = 4, //! 32-bit ARM ISA (little endian). kARM = 5, //! 64-bit ARM ISA in (little endian). kAArch64 = 6, //! 32-bit ARM ISA in Thumb mode (little endian). kThumb = 7, // 8 is not used at the moment, even numbers are 64-bit architectures. //! 32-bit MIPS ISA in (little endian). kMIPS32_LE = 9, //! 64-bit MIPS ISA in (little endian). kMIPS64_LE = 10, //! 32-bit ARM ISA (big endian). kARM_BE = 11, //! 64-bit ARM ISA in (big endian). kAArch64_BE = 12, //! 32-bit ARM ISA in Thumb mode (big endian). kThumb_BE = 13, // 14 is not used at the moment, even numbers are 64-bit architectures. //! 32-bit MIPS ISA in (big endian). kMIPS32_BE = 15, //! 64-bit MIPS ISA in (big endian). kMIPS64_BE = 16, //! Maximum value of `Arch`. kMaxValue = kMIPS64_BE, //! Mask used by 32-bit ISAs (odd are 32-bit, even are 64-bit). k32BitMask = 0x01, //! First big-endian architecture. kBigEndian = kARM_BE, //! ISA detected at compile-time (ISA of the host). kHost = #if defined(_DOXYGEN) DETECTED_AT_COMPILE_TIME #else ASMJIT_ARCH_X86 == 32 ? kX86 : ASMJIT_ARCH_X86 == 64 ? kX64 : ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_LE ? kARM : ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_BE ? kARM_BE : ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kAArch64 : ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_BE ? kAArch64_BE : ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kMIPS32_LE : ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kMIPS32_BE : ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kMIPS64_LE : ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kMIPS64_BE : kUnknown #endif }; //! Sub-architecture. enum class SubArch : uint8_t { //! Unknown or uninitialized architecture sub-type. kUnknown = 0, //! Maximum value of `SubArch`. kMaxValue = kUnknown, //! Sub-architecture detected at compile-time (sub-architecture of the host). kHost = #if defined(_DOXYGEN) DETECTED_AT_COMPILE_TIME #else kUnknown #endif }; //! Identifier used to represent names of different data types across architectures. enum class ArchTypeNameId : uint8_t { //! Describes 'db' (X86/X86_64 convention, always 8-bit quantity). kDB = 0, //! Describes 'dw' (X86/X86_64 convention, always 16-bit word). kDW, //! Describes 'dd' (X86/X86_64 convention, always 32-bit word). kDD, //! Describes 'dq' (X86/X86_64 convention, always 64-bit word). kDQ, //! Describes 'byte' (always 8-bit quantity). kByte, //! Describes 'half' (most likely 16-bit word). kHalf, //! Describes 'word' (either 16-bit or 32-bit word). kWord, //! Describes 'hword' (most likely 16-bit word). kHWord, //! Describes 'dword' (either 32-bit or 64-bit word). kDWord, //! Describes 'qword' (64-bit word). kQWord, //! Describes 'xword' (64-bit word). kXWord, //! Describes 'short' (always 16-bit word). kShort, //! Describes 'long' (most likely 32-bit word). kLong, //! Describes 'quad' (64-bit word). kQuad, //! Maximum value of `ArchTypeNameId`. kMaxValue = kQuad }; //! Instruction feature hints for each register group provided by \ref ArchTraits. //! //! Instruction feature hints describe miscellaneous instructions provided by the architecture that can be used by //! register allocator to make certain things simpler - like register swaps or emitting register push/pop sequences. //! //! \remarks Instruction feature hints are only defined for register groups that can be used with \ref //! asmjit_compiler infrastructure. Register groups that are not managed by Compiler are not provided by //! \ref ArchTraits and cannot be queried. enum class InstHints : uint8_t { //! No feature hints. kNoHints = 0, //! Architecture supports a register swap by using a single instruction. kRegSwap = 0x01u, //! Architecture provides push/pop instructions. kPushPop = 0x02u }; ASMJIT_DEFINE_ENUM_FLAGS(InstHints) //! Architecture traits used by Function API and Compiler's register allocator. struct ArchTraits { //! \name Members //! \{ //! 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. Support::Array _instHints; //! Maps register type into a signature, that provides group, size and can be used to construct register operands. Support::Array _regSignature; //! Maps a register to type-id, see \ref TypeId. Support::Array _regTypeToTypeId; //! Maps scalar TypeId values (from TypeId::_kIdBaseStart) to register types, see \ref TypeId. Support::Array _typeIdToRegType; //! Word name identifiers of 8-bit, 16-bit, 32-biit, and 64-bit quantities that appear in formatted text. ArchTypeNameId _typeNameIdTable[4]; //! \} //! \name Accessors //! \{ //! Returns stack pointer register id. inline uint32_t spRegId() const noexcept { return _spRegId; } //! Returns stack frame register id. inline uint32_t fpRegId() const noexcept { return _fpRegId; } //! Returns link register id, if the architecture provides it. inline uint32_t linkRegId() const noexcept { return _linkRegId; } //! Returns instruction pointer register id, if the architecture provides it. inline 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 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 bool hasLinkReg() const noexcept { return _linkRegId != BaseReg::kIdBad; } //! Returns minimum addressable offset on stack guaranteed for all instructions. inline uint32_t minStackOffset() const noexcept { return _minStackOffset; } //! Returns maximum addressable offset on stack depending on specific instruction. inline uint32_t maxStackOffset() const noexcept { return _maxStackOffset; } //! Returns ISA flags of the given register `group`. inline InstHints instFeatureHints(RegGroup group) const noexcept { return _instHints[group]; } //! Tests whether the given register `group` has the given `flag` set. inline bool hasInstHint(RegGroup group, InstHints feature) const noexcept { return Support::test(_instHints[group], feature); } //! Tests whether the ISA provides register swap instruction for the given register `group`. inline bool hasInstRegSwap(RegGroup group) const noexcept { return hasInstHint(group, InstHints::kRegSwap); } //! Tests whether the ISA provides push/pop instructions for the given register `group`. inline bool hasInstPushPop(RegGroup group) const noexcept { return hasInstHint(group, InstHints::kPushPop); } inline bool hasRegType(RegType type) const noexcept { return type <= RegType::kMaxValue && _regSignature[type].isValid(); } //! Returns an operand signature from the given register `type` of this architecture. inline OperandSignature regTypeToSignature(RegType type) const noexcept { return _regSignature[type]; } //! Returns a register from the given register `type` of this architecture. inline RegGroup regTypeToGroup(RegType type) const noexcept { return _regSignature[type].regGroup(); } //! Returns a register size the given register `type` of this architecture. inline uint32_t regTypeToSize(RegType type) const noexcept { return _regSignature[type].size(); } //! Returns a corresponding `TypeId` from the given register `type` of this architecture. inline TypeId regTypeToTypeId(RegType type) const noexcept { return _regTypeToTypeId[type]; } //! Returns a table of ISA word names that appear in formatted text. Word names are ISA dependent. //! //! The index of this table is log2 of the size: //! - [0] 8-bits //! - [1] 16-bits //! - [2] 32-bits //! - [3] 64-bits inline const ArchTypeNameId* typeNameIdTable() const noexcept { return _typeNameIdTable; } //! Returns an ISA word name identifier of the given `index`, see \ref typeNameIdTable() for more details. inline ArchTypeNameId typeNameIdByIndex(uint32_t index) const noexcept { return _typeNameIdTable[index]; } //! \} //! \name Statics //! \{ //! Returns a const reference to `ArchTraits` for the given architecture `arch`. static inline const ArchTraits& byArch(Arch arch) noexcept; //! \} }; ASMJIT_VARAPI const ArchTraits _archTraits[uint32_t(Arch::kMaxValue) + 1]; //! \cond inline const ArchTraits& ArchTraits::byArch(Arch arch) noexcept { return _archTraits[uint32_t(arch)]; } //! \endcond //! Architecture utilities. namespace ArchUtils { ASMJIT_API Error typeIdToRegSignature(Arch arch, TypeId typeId, TypeId* typeIdOut, OperandSignature* regSignatureOut) noexcept; } // {ArchUtils} //! \} ASMJIT_END_NAMESPACE #endif // ASMJIT_CORE_ARCHTRAITS_H_INCLUDED