Unverified Commit 6e3c2142 authored by peastman's avatar peastman Committed by GitHub
Browse files

Merge pull request #2289 from peastman/asmjit

Upgraded to latest version of asmjit
parents b6e79d4a 0e0558b9
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CODEHOLDER_H
#define _ASMJIT_BASE_CODEHOLDER_H
// [Dependencies]
#include "../base/arch.h"
#include "../base/func.h"
#include "../base/logging.h"
#include "../base/operand.h"
#include "../base/simdtypes.h"
#include "../base/utils.h"
#include "../base/zone.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [Forward Declarations]
// ============================================================================
class Assembler;
class CodeEmitter;
class CodeHolder;
// ============================================================================
// [asmjit::AlignMode]
// ============================================================================
//! Align mode.
ASMJIT_ENUM(AlignMode) {
kAlignCode = 0, //!< Align executable code.
kAlignData = 1, //!< Align non-executable code.
kAlignZero = 2, //!< Align by a sequence of zeros.
kAlignCount //!< Count of alignment modes.
};
// ============================================================================
// [asmjit::ErrorHandler]
// ============================================================================
//! Error handler can be used to override the default behavior of error handling
//! available to all classes that inherit \ref CodeEmitter. See \ref handleError().
class ASMJIT_VIRTAPI ErrorHandler {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `ErrorHandler` instance.
ASMJIT_API ErrorHandler() noexcept;
//! Destroy the `ErrorHandler` instance.
ASMJIT_API virtual ~ErrorHandler() noexcept;
// --------------------------------------------------------------------------
// [Handle Error]
// --------------------------------------------------------------------------
//! Error handler (abstract).
//!
//! Error handler is called after an error happened and before it's propagated
//! to the caller. There are multiple ways how the error handler can be used:
//!
//! 1. Returning `true` or `false` from `handleError()`. If `true` is returned
//! it means that the error was reported and AsmJit can continue execution.
//! The reported error still be propagated to the caller, but won't put the
//! CodeEmitter into an error state (it won't set last-error). However,
//! returning `false` means that the error cannot be handled - in such case
//! it stores the error, which can be then retrieved by using `getLastError()`.
//! Returning `false` is the default behavior when no error handler is present.
//! To put the assembler into a non-error state again a `resetLastError()` must
//! be called.
//!
//! 2. Throwing an exception. AsmJit doesn't use exceptions and is completely
//! exception-safe, but you can throw exception from your error handler if
//! this way is the preferred way of handling errors in your project. Throwing
//! an exception acts virtually as returning `true` as AsmJit won't be able
//! to store the error because the exception changes execution path.
//!
//! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts
//! `CodeEmitter` to a consistent state before calling the `handleError()`
//! so `longjmp()` can be used without any issues to cancel the code
//! generation if an error occurred. There is no difference between
//! exceptions and longjmp() from AsmJit's perspective.
virtual bool handleError(Error err, const char* message, CodeEmitter* origin) = 0;
};
// ============================================================================
// [asmjit::CodeInfo]
// ============================================================================
//! Basic information about a code (or target). It describes its architecture,
//! code generation mode (or optimization level), and base address.
class CodeInfo {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CodeInfo() noexcept
: _archInfo(),
_stackAlignment(0),
_cdeclCallConv(CallConv::kIdNone),
_stdCallConv(CallConv::kIdNone),
_fastCallConv(CallConv::kIdNone),
_baseAddress(Globals::kNoBaseAddress) {}
ASMJIT_INLINE CodeInfo(const CodeInfo& other) noexcept { init(other); }
explicit ASMJIT_INLINE CodeInfo(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept
: _archInfo(archType, archMode),
_packedMiscInfo(0),
_baseAddress(baseAddress) {}
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isInitialized() const noexcept {
return _archInfo._type != ArchInfo::kTypeNone;
}
ASMJIT_INLINE void init(const CodeInfo& other) noexcept {
_archInfo = other._archInfo;
_packedMiscInfo = other._packedMiscInfo;
_baseAddress = other._baseAddress;
}
ASMJIT_INLINE void init(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept {
_archInfo.init(archType, archMode);
_packedMiscInfo = 0;
_baseAddress = baseAddress;
}
ASMJIT_INLINE void reset() noexcept {
_archInfo.reset();
_stackAlignment = 0;
_cdeclCallConv = CallConv::kIdNone;
_stdCallConv = CallConv::kIdNone;
_fastCallConv = CallConv::kIdNone;
_baseAddress = Globals::kNoBaseAddress;
}
// --------------------------------------------------------------------------
// [Architecture Information]
// --------------------------------------------------------------------------
//! Get architecture information, see \ref ArchInfo.
ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; }
//! Get architecture type, see \ref ArchInfo::Type.
ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); }
//! Get architecture sub-type, see \ref ArchInfo::SubType.
ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); }
//! Get a size of a GP register of the architecture the code is using.
ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _archInfo.getGpSize(); }
//! Get number of GP registers available of the architecture the code is using.
ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _archInfo.getGpCount(); }
// --------------------------------------------------------------------------
// [High-Level Information]
// --------------------------------------------------------------------------
//! Get a natural stack alignment that must be honored (or 0 if not known).
ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; }
//! Set a natural stack alignment that must be honored.
ASMJIT_INLINE void setStackAlignment(uint8_t sa) noexcept { _stackAlignment = static_cast<uint8_t>(sa); }
ASMJIT_INLINE uint32_t getCdeclCallConv() const noexcept { return _cdeclCallConv; }
ASMJIT_INLINE void setCdeclCallConv(uint32_t cc) noexcept { _cdeclCallConv = static_cast<uint8_t>(cc); }
ASMJIT_INLINE uint32_t getStdCallConv() const noexcept { return _stdCallConv; }
ASMJIT_INLINE void setStdCallConv(uint32_t cc) noexcept { _stdCallConv = static_cast<uint8_t>(cc); }
ASMJIT_INLINE uint32_t getFastCallConv() const noexcept { return _fastCallConv; }
ASMJIT_INLINE void setFastCallConv(uint32_t cc) noexcept { _fastCallConv = static_cast<uint8_t>(cc); }
// --------------------------------------------------------------------------
// [Addressing Information]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _baseAddress != Globals::kNoBaseAddress; }
ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _baseAddress; }
ASMJIT_INLINE void setBaseAddress(uint64_t p) noexcept { _baseAddress = p; }
ASMJIT_INLINE void resetBaseAddress() noexcept { _baseAddress = Globals::kNoBaseAddress; }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE CodeInfo& operator=(const CodeInfo& other) noexcept { init(other); return *this; }
ASMJIT_INLINE bool operator==(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) == 0; }
ASMJIT_INLINE bool operator!=(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) != 0; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
ArchInfo _archInfo; //!< Architecture information.
union {
struct {
uint8_t _stackAlignment; //!< Natural stack alignment (ARCH+OS).
uint8_t _cdeclCallConv; //!< Default CDECL calling convention.
uint8_t _stdCallConv; //!< Default STDCALL calling convention.
uint8_t _fastCallConv; //!< Default FASTCALL calling convention.
};
uint32_t _packedMiscInfo; //!< \internal
};
uint64_t _baseAddress; //!< Base address.
};
// ============================================================================
// [asmjit::CodeBuffer]
// ============================================================================
//! Code or data buffer.
struct CodeBuffer {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool hasData() const noexcept { return _data != nullptr; }
ASMJIT_INLINE uint8_t* getData() noexcept { return _data; }
ASMJIT_INLINE const uint8_t* getData() const noexcept { return _data; }
ASMJIT_INLINE size_t getLength() const noexcept { return _length; }
ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; }
ASMJIT_INLINE bool isExternal() const noexcept { return _isExternal; }
ASMJIT_INLINE bool isFixedSize() const noexcept { return _isFixedSize; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t* _data; //!< The content of the buffer (data).
size_t _length; //!< Number of bytes of `data` used.
size_t _capacity; //!< Buffer capacity (in bytes).
bool _isExternal; //!< True if this is external buffer.
bool _isFixedSize; //!< True if this buffer cannot grow.
};
// ============================================================================
// [asmjit::SectionEntry]
// ============================================================================
//! Section entry.
class SectionEntry {
public:
ASMJIT_ENUM(Id) {
kInvalidId = 0xFFFFFFFFU //!< Invalid section id.
};
//! Section flags.
ASMJIT_ENUM(Flags) {
kFlagExec = 0x00000001U, //!< Executable (.text sections).
kFlagConst = 0x00000002U, //!< Read-only (.text and .data sections).
kFlagZero = 0x00000004U, //!< Zero initialized by the loader (BSS).
kFlagInfo = 0x00000008U, //!< Info / comment flag.
kFlagImplicit = 0x80000000U //!< Section created implicitly (can be deleted by the Runtime).
};
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }
ASMJIT_INLINE const char* getName() const noexcept { return _name; }
ASMJIT_INLINE void _setDefaultName(
char c0 = 0, char c1 = 0, char c2 = 0, char c3 = 0,
char c4 = 0, char c5 = 0, char c6 = 0, char c7 = 0) noexcept {
_nameAsU32[0] = Utils::pack32_4x8(c0, c1, c2, c3);
_nameAsU32[1] = Utils::pack32_4x8(c4, c5, c6, c7);
}
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; }
ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
ASMJIT_INLINE void addFlags(uint32_t flags) noexcept { _flags |= flags; }
ASMJIT_INLINE void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; }
ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; }
ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; }
ASMJIT_INLINE size_t getPhysicalSize() const noexcept { return _buffer.getLength(); }
ASMJIT_INLINE size_t getVirtualSize() const noexcept { return _virtualSize; }
ASMJIT_INLINE void setVirtualSize(uint32_t size) noexcept { _virtualSize = size; }
ASMJIT_INLINE CodeBuffer& getBuffer() noexcept { return _buffer; }
ASMJIT_INLINE const CodeBuffer& getBuffer() const noexcept { return _buffer; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint32_t _id; //!< Section id.
uint32_t _flags; //!< Section flags.
uint32_t _alignment; //!< Section alignment requirements (0 if no requirements).
uint32_t _virtualSize; //!< Virtual size of the section (zero initialized mostly).
union {
char _name[36]; //!< Section name (max 35 characters, PE allows max 8).
uint32_t _nameAsU32[36 / 4]; //!< Section name as `uint32_t[]` (only optimization).
};
CodeBuffer _buffer; //!< Code or data buffer.
};
// ============================================================================
// [asmjit::LabelLink]
// ============================================================================
//! Data structure used to link labels.
struct LabelLink {
LabelLink* prev; //!< Previous link (single-linked list).
uint32_t sectionId; //!< Section id.
uint32_t relocId; //!< Relocation id or RelocEntry::kInvalidId.
size_t offset; //!< Label offset relative to the start of the section.
intptr_t rel; //!< Inlined rel8/rel32.
};
// ============================================================================
// [asmjit::LabelEntry]
// ============================================================================
//! Label entry.
//!
//! Contains the following properties:
//! * Label id - This is the only thing that is set to the `Label` operand.
//! * Label name - Optional, used mostly to create executables and libraries.
//! * Label type - Type of the label, default `Label::kTypeAnonymous`.
//! * Label parent id - Derived from many assemblers that allow to define a
//! local label that falls under a global label. This allows to define
//! many labels of the same name that have different parent (global) label.
//! * Offset - offset of the label bound by `Assembler`.
//! * Links - single-linked list that contains locations of code that has
//! to be patched when the label gets bound. Every use of unbound label
//! adds one link to `_links` list.
//! * HVal - Hash value of label's name and optionally parentId.
//! * HashNext - Hash-table implementation detail.
class LabelEntry : public ZoneHashNode {
public:
// NOTE: Label id is stored in `_customData`, which is provided by ZoneHashNode
// to fill a padding that a C++ compiler targeting 64-bit CPU will add to align
// the structure to 64-bits.
//! Get label id.
ASMJIT_INLINE uint32_t getId() const noexcept { return _customData; }
//! Set label id (internal, used only by \ref CodeHolder).
ASMJIT_INLINE void _setId(uint32_t id) noexcept { _customData = id; }
//! Get label type, see \ref Label::Type.
ASMJIT_INLINE uint32_t getType() const noexcept { return _type; }
//! Get label flags, returns 0 at the moment.
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; }
ASMJIT_INLINE bool hasParent() const noexcept { return _parentId != 0; }
//! Get label's parent id.
ASMJIT_INLINE uint32_t getParentId() const noexcept { return _parentId; }
//! Get label's section id where it's bound to (or `SectionEntry::kInvalidId` if it's not bound yet).
ASMJIT_INLINE uint32_t getSectionId() const noexcept { return _sectionId; }
//! Get if the label has name.
ASMJIT_INLINE bool hasName() const noexcept { return !_name.isEmpty(); }
//! Get the label's name.
//!
//! NOTE: Local labels will return their local name without their parent
//! part, for example ".L1".
ASMJIT_INLINE const char* getName() const noexcept { return _name.getData(); }
//! Get length of label's name.
//!
//! NOTE: Label name is always null terminated, so you can use `strlen()` to
//! get it, however, it's also cached in `LabelEntry`, so if you want to know
//! the length the easiest way is to use `LabelEntry::getNameLength()`.
ASMJIT_INLINE size_t getNameLength() const noexcept { return _name.getLength(); }
//! Get if the label is bound.
ASMJIT_INLINE bool isBound() const noexcept { return _sectionId != SectionEntry::kInvalidId; }
//! Get the label offset (only useful if the label is bound).
ASMJIT_INLINE intptr_t getOffset() const noexcept { return _offset; }
//! Get the hash-value of label's name and its parent label (if any).
//!
//! Label hash is calculated as `HASH(Name) ^ ParentId`. The hash function
//! is implemented in `Utils::hashString()` and `Utils::hashRound()`.
ASMJIT_INLINE uint32_t getHVal() const noexcept { return _hVal; }
// ------------------------------------------------------------------------
// [Members]
// ------------------------------------------------------------------------
// Let's round the size of `LabelEntry` to 64 bytes (as ZoneHeap has 32
// bytes granularity anyway). This gives `_name` the remaining space, which
// is roughly 16 bytes on 64-bit and 28 bytes on 32-bit architectures.
enum { kNameBytes = 64 - (sizeof(ZoneHashNode) + 16 + sizeof(intptr_t) + sizeof(LabelLink*)) };
uint8_t _type; //!< Label type, see Label::Type.
uint8_t _flags; //!< Must be zero.
uint16_t _reserved16; //!< Reserved.
uint32_t _parentId; //!< Label parent id or zero.
uint32_t _sectionId; //!< Section id or `SectionEntry::kInvalidId`.
uint32_t _reserved32; //!< Reserved.
intptr_t _offset; //!< Label offset.
LabelLink* _links; //!< Label links.
SmallString<kNameBytes> _name; //!< Label name.
};
// ============================================================================
// [asmjit::RelocEntry]
// ============================================================================
//! Relocation entry.
struct RelocEntry {
ASMJIT_ENUM(Id) {
kInvalidId = 0xFFFFFFFFU //!< Invalid relocation id.
};
//! Relocation type.
ASMJIT_ENUM(Type) {
kTypeNone = 0, //!< Deleted entry (no relocation).
kTypeAbsToAbs = 1, //!< Relocate absolute to absolute.
kTypeRelToAbs = 2, //!< Relocate relative to absolute.
kTypeAbsToRel = 3, //!< Relocate absolute to relative.
kTypeTrampoline = 4 //!< Relocate absolute to relative or use trampoline.
};
// ------------------------------------------------------------------------
// [Accessors]
// ------------------------------------------------------------------------
ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }
ASMJIT_INLINE uint32_t getType() const noexcept { return _type; }
ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; }
ASMJIT_INLINE uint32_t getSourceSectionId() const noexcept { return _sourceSectionId; }
ASMJIT_INLINE uint32_t getTargetSectionId() const noexcept { return _targetSectionId; }
ASMJIT_INLINE uint64_t getSourceOffset() const noexcept { return _sourceOffset; }
ASMJIT_INLINE uint64_t getData() const noexcept { return _data; }
// ------------------------------------------------------------------------
// [Members]
// ------------------------------------------------------------------------
uint32_t _id; //!< Relocation id.
uint8_t _type; //!< Type of the relocation.
uint8_t _size; //!< Size of the relocation (1, 2, 4 or 8 bytes).
uint8_t _reserved[2]; //!< Reserved.
uint32_t _sourceSectionId; //!< Source section id.
uint32_t _targetSectionId; //!< Destination section id.
uint64_t _sourceOffset; //!< Source offset (relative to start of the section).
uint64_t _data; //!< Relocation data (target offset, target address, etc).
};
// ============================================================================
// [asmjit::CodeHolder]
// ============================================================================
//! Contains basic information about the target architecture plus its settings,
//! and holds code & data (including sections, labels, and relocation information).
//! CodeHolder can store both binary and intermediate representation of assembly,
//! which can be generated by \ref Assembler and/or \ref CodeBuilder.
//!
//! NOTE: CodeHolder has ability to attach an \ref ErrorHandler, however, this
//! error handler is not triggered by CodeHolder itself, it's only used by the
//! attached code generators.
class CodeHolder {
public:
ASMJIT_NONCOPYABLE(CodeHolder)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create an uninitialized CodeHolder (you must init() it before it can be used).
ASMJIT_API CodeHolder() noexcept;
//! Destroy the CodeHolder.
ASMJIT_API ~CodeHolder() noexcept;
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isInitialized() const noexcept { return _codeInfo.isInitialized(); }
//! Initialize to CodeHolder to hold code described by `codeInfo`.
ASMJIT_API Error init(const CodeInfo& info) noexcept;
//! Detach all code-generators attached and reset the \ref CodeHolder.
ASMJIT_API void reset(bool releaseMemory = false) noexcept;
// --------------------------------------------------------------------------
// [Attach / Detach]
// --------------------------------------------------------------------------
//! Attach a \ref CodeEmitter to this \ref CodeHolder.
ASMJIT_API Error attach(CodeEmitter* emitter) noexcept;
//! Detach a \ref CodeEmitter from this \ref CodeHolder.
ASMJIT_API Error detach(CodeEmitter* emitter) noexcept;
// --------------------------------------------------------------------------
// [Sync]
// --------------------------------------------------------------------------
//! Synchronize all states of all `CodeEmitter`s associated with the CodeHolder.
//! This is required as some code generators don't sync every time they do
//! something - for example \ref Assembler generally syncs when it needs to
//! reallocate the \ref CodeBuffer, but not each time it encodes instruction
//! or directive.
ASMJIT_API void sync() noexcept;
// --------------------------------------------------------------------------
// [Code-Information]
// --------------------------------------------------------------------------
//! Get code/target information, see \ref CodeInfo.
ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; }
//! Get architecture information, see \ref ArchInfo.
ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); }
//! Get the target's architecture type.
ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); }
//! Get the target's architecture sub-type.
ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); }
//! Get if a static base-address is set.
ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _codeInfo.hasBaseAddress(); }
//! Get a static base-address (uint64_t).
ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _codeInfo.getBaseAddress(); }
// --------------------------------------------------------------------------
// [Global Information]
// --------------------------------------------------------------------------
//! Get global hints, internally propagated to all `CodeEmitter`s attached.
ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; }
//! Get global options, internally propagated to all `CodeEmitter`s attached.
ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; }
// --------------------------------------------------------------------------
// [Result Information]
// --------------------------------------------------------------------------
//! Get the size code & data of all sections.
ASMJIT_API size_t getCodeSize() const noexcept;
//! Get size of all possible trampolines.
//!
//! Trampolines are needed to successfully generate relative jumps to absolute
//! addresses. This value is only non-zero if jmp of call instructions were
//! used with immediate operand (this means jumping or calling an absolute
//! address directly).
ASMJIT_INLINE size_t getTrampolinesSize() const noexcept { return _trampolinesSize; }
// --------------------------------------------------------------------------
// [Logging & Error Handling]
// --------------------------------------------------------------------------
#if !defined(ASMJIT_DISABLE_LOGGING)
//! Get if a logger attached.
ASMJIT_INLINE bool hasLogger() const noexcept { return _logger != nullptr; }
//! Get the attached logger.
ASMJIT_INLINE Logger* getLogger() const noexcept { return _logger; }
//! Attach a `logger` to CodeHolder and propagate it to all attached `CodeEmitter`s.
ASMJIT_API void setLogger(Logger* logger) noexcept;
//! Reset the logger (does nothing if not attached).
ASMJIT_INLINE void resetLogger() noexcept { setLogger(nullptr); }
#endif // !ASMJIT_DISABLE_LOGGING
//! Get if error-handler is attached.
ASMJIT_INLINE bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; }
//! Get the error-handler.
ASMJIT_INLINE ErrorHandler* getErrorHandler() const noexcept { return _errorHandler; }
//! Set the error handler, will affect all attached `CodeEmitter`s.
ASMJIT_API Error setErrorHandler(ErrorHandler* handler) noexcept;
//! Reset the error handler (does nothing if not attached).
ASMJIT_INLINE void resetErrorHandler() noexcept { setErrorHandler(nullptr); }
// --------------------------------------------------------------------------
// [Sections]
// --------------------------------------------------------------------------
//! Get array of `SectionEntry*` records.
ASMJIT_INLINE const ZoneVector<SectionEntry*>& getSections() const noexcept { return _sections; }
//! Get a section entry of the given index.
ASMJIT_INLINE SectionEntry* getSectionEntry(size_t index) const noexcept { return _sections[index]; }
ASMJIT_API Error growBuffer(CodeBuffer* cb, size_t n) noexcept;
ASMJIT_API Error reserveBuffer(CodeBuffer* cb, size_t n) noexcept;
// --------------------------------------------------------------------------
// [Labels & Symbols]
// --------------------------------------------------------------------------
//! Create a new anonymous label and return its id in `idOut`.
//!
//! Returns `Error`, does not report error to \ref ErrorHandler.
ASMJIT_API Error newLabelId(uint32_t& idOut) noexcept;
//! Create a new named label label-type `type`.
//!
//! Returns `Error`, does not report error to \ref ErrorHandler.
ASMJIT_API Error newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept;
//! Get a label id by name.
ASMJIT_API uint32_t getLabelIdByName(const char* name, size_t nameLength = Globals::kInvalidIndex, uint32_t parentId = 0) noexcept;
//! Create a new label-link used to store information about yet unbound labels.
//!
//! Returns `null` if the allocation failed.
ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept;
//! Get array of `LabelEntry*` records.
ASMJIT_INLINE const ZoneVector<LabelEntry*>& getLabelEntries() const noexcept { return _labels; }
//! Get number of labels created.
ASMJIT_INLINE size_t getLabelsCount() const noexcept { return _labels.getLength(); }
//! Get number of label references, which are unresolved at the moment.
ASMJIT_INLINE size_t getUnresolvedLabelsCount() const noexcept { return _unresolvedLabelsCount; }
//! Get if the `label` is valid (i.e. created by `newLabelId()`).
ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept {
return isLabelValid(label.getId());
}
//! Get if the label having `id` is valid (i.e. created by `newLabelId()`).
ASMJIT_INLINE bool isLabelValid(uint32_t labelId) const noexcept {
size_t index = Operand::unpackId(labelId);
return index < _labels.getLength();
}
//! Get if the `label` is already bound.
//!
//! Returns `false` if the `label` is not valid.
ASMJIT_INLINE bool isLabelBound(const Label& label) const noexcept {
return isLabelBound(label.getId());
}
//! \overload
ASMJIT_INLINE bool isLabelBound(uint32_t id) const noexcept {
size_t index = Operand::unpackId(id);
return index < _labels.getLength() && _labels[index]->isBound();
}
//! Get a `label` offset or -1 if the label is not yet bound.
ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const noexcept {
return getLabelOffset(label.getId());
}
//! \overload
ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const noexcept {
ASMJIT_ASSERT(isLabelValid(id));
return _labels[Operand::unpackId(id)]->getOffset();
}
//! Get information about the given `label`.
ASMJIT_INLINE LabelEntry* getLabelEntry(const Label& label) const noexcept {
return getLabelEntry(label.getId());
}
//! Get information about a label having the given `id`.
ASMJIT_INLINE LabelEntry* getLabelEntry(uint32_t id) const noexcept {
size_t index = static_cast<size_t>(Operand::unpackId(id));
return index < _labels.getLength() ? _labels[index] : static_cast<LabelEntry*>(nullptr);
}
// --------------------------------------------------------------------------
// [Relocations]
// --------------------------------------------------------------------------
//! Create a new relocation entry of type `type` and size `size`.
//!
//! Additional fields can be set after the relocation entry was created.
ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept;
//! Get if the code contains relocations.
ASMJIT_INLINE bool hasRelocations() const noexcept { return !_relocations.isEmpty(); }
//! Get array of `RelocEntry*` records.
ASMJIT_INLINE const ZoneVector<RelocEntry*>& getRelocEntries() const noexcept { return _relocations; }
ASMJIT_INLINE RelocEntry* getRelocEntry(uint32_t id) const noexcept { return _relocations[id]; }
//! Relocate the code to `baseAddress` and copy it to `dst`.
//!
//! \param dst Contains the location where the relocated code should be
//! copied. The pointer can be address returned by virtual memory allocator
//! or any other address that has sufficient space.
//!
//! \param baseAddress Base address used for relocation. `JitRuntime` always
//! sets the `baseAddress` to be the same as `dst`.
//!
//! \return The number bytes actually used. If the code emitter reserved
//! space for possible trampolines, but didn't use it, the number of bytes
//! used can actually be less than the expected worst case. Virtual memory
//! allocator can shrink the memory it allocated initially.
//!
//! A given buffer will be overwritten, to get the number of bytes required,
//! use `getCodeSize()`.
ASMJIT_API size_t relocate(void* dst, uint64_t baseAddress = Globals::kNoBaseAddress) const noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
CodeInfo _codeInfo; //!< Basic information about the code (architecture and other info).
uint32_t _globalHints; //!< Global hints, propagated to all `CodeEmitter`s.
uint32_t _globalOptions; //!< Global options, propagated to all `CodeEmitter`s.
CodeEmitter* _emitters; //!< Linked-list of all attached `CodeEmitter`s.
Assembler* _cgAsm; //!< Attached \ref Assembler (only one at a time).
Logger* _logger; //!< Attached \ref Logger, used by all consumers.
ErrorHandler* _errorHandler; //!< Attached \ref ErrorHandler.
uint32_t _unresolvedLabelsCount; //!< Count of label references which were not resolved.
uint32_t _trampolinesSize; //!< Size of all possible trampolines.
Zone _baseZone; //!< Base zone (used to allocate core structures).
Zone _dataZone; //!< Data zone (used to allocate extra data like label names).
ZoneHeap _baseHeap; //!< Zone allocator, used to manage internal containers.
ZoneVector<SectionEntry*> _sections; //!< Section entries.
ZoneVector<LabelEntry*> _labels; //!< Label entries (each label is stored here).
ZoneVector<RelocEntry*> _relocations; //!< Relocation entries.
ZoneHash<LabelEntry> _namedLabels; //!< Label name -> LabelEntry (only named labels).
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CODEHOLDER_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies - AsmJit]
#include "../base/assembler.h"
#include "../base/compiler.h"
#include "../base/context_p.h"
#include "../base/cpuinfo.h"
#include "../base/intutil.h"
#include "../base/logger.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Constants]
// ============================================================================
static const char noName[1] = { '\0' };
enum { kBaseCompilerDefaultLookAhead = 64 };
// ============================================================================
// [asmjit::Compiler - Construction / Destruction]
// ============================================================================
Compiler::Compiler(Runtime* runtime) :
CodeGen(runtime),
_assembler(NULL),
_nodeFlowId(0),
_nodeFlags(0),
_maxLookAhead(kBaseCompilerDefaultLookAhead),
_targetVarMapping(NULL),
_firstNode(NULL),
_lastNode(NULL),
_cursor(NULL),
_func(NULL),
_varZone(4096 - kZoneOverhead),
_stringZone(4096 - kZoneOverhead),
_localConstZone(4096 - kZoneOverhead),
_localConstPool(&_localConstZone),
_globalConstPool(&_baseZone) {}
Compiler::~Compiler() {
reset(true);
if (_assembler != NULL)
delete _assembler;
}
// ============================================================================
// [asmjit::Compiler - Clear / Reset]
// ============================================================================
void Compiler::reset(bool releaseMemory) {
// CodeGen members.
_baseAddress = kNoBaseAddress;
_instOptions = 0;
_error = kErrorOk;
_baseZone.reset(releaseMemory);
// Compiler members.
_nodeFlowId = 0;
_nodeFlags = 0;
if (_assembler != NULL)
_assembler->reset(releaseMemory);
_firstNode = NULL;
_lastNode = NULL;
_cursor = NULL;
_func = NULL;
_localConstPool.reset();
_globalConstPool.reset();
_localConstPoolLabel.reset();
_globalConstPoolLabel.reset();
_varZone.reset(releaseMemory);
_stringZone.reset(releaseMemory);
_localConstZone.reset(releaseMemory);
_targetList.reset(releaseMemory);
_varList.reset(releaseMemory);
}
// ============================================================================
// [asmjit::Compiler - Node Management]
// ============================================================================
Node* Compiler::setCursor(Node* node) {
Node* old = _cursor;
_cursor = node;
return old;
}
Node* Compiler::addNode(Node* node) {
ASMJIT_ASSERT(node != NULL);
ASMJIT_ASSERT(node->_prev == NULL);
ASMJIT_ASSERT(node->_next == NULL);
if (_cursor == NULL) {
if (_firstNode == NULL) {
_firstNode = node;
_lastNode = node;
}
else {
node->_next = _firstNode;
_firstNode->_prev = node;
_firstNode = node;
}
}
else {
Node* prev = _cursor;
Node* next = _cursor->_next;
node->_prev = prev;
node->_next = next;
prev->_next = node;
if (next)
next->_prev = node;
else
_lastNode = node;
}
_cursor = node;
return node;
}
Node* Compiler::addNodeBefore(Node* node, Node* ref) {
ASMJIT_ASSERT(node != NULL);
ASMJIT_ASSERT(node->_prev == NULL);
ASMJIT_ASSERT(node->_next == NULL);
ASMJIT_ASSERT(ref != NULL);
Node* prev = ref->_prev;
Node* next = ref;
node->_prev = prev;
node->_next = next;
next->_prev = node;
if (prev)
prev->_next = node;
else
_firstNode = node;
return node;
}
Node* Compiler::addNodeAfter(Node* node, Node* ref) {
ASMJIT_ASSERT(node != NULL);
ASMJIT_ASSERT(node->_prev == NULL);
ASMJIT_ASSERT(node->_next == NULL);
ASMJIT_ASSERT(ref != NULL);
Node* prev = ref;
Node* next = ref->_next;
node->_prev = prev;
node->_next = next;
prev->_next = node;
if (next)
next->_prev = node;
else
_lastNode = node;
return node;
}
static ASMJIT_INLINE void BaseCompiler_nodeRemoved(Compiler* self, Node* node_) {
if (node_->isJmpOrJcc()) {
JumpNode* node = static_cast<JumpNode*>(node_);
TargetNode* target = node->getTarget();
// Disconnect.
JumpNode** pPrev = &target->_from;
for (;;) {
ASMJIT_ASSERT(*pPrev != NULL);
JumpNode* current = *pPrev;
if (current == NULL)
break;
if (current == node) {
*pPrev = node->_jumpNext;
break;
}
pPrev = &current->_jumpNext;
}
target->subNumRefs();
}
}
Node* Compiler::removeNode(Node* node) {
Node* prev = node->_prev;
Node* next = node->_next;
if (_firstNode == node)
_firstNode = next;
else
prev->_next = next;
if (_lastNode == node)
_lastNode = prev;
else
next->_prev = prev;
node->_prev = NULL;
node->_next = NULL;
if (_cursor == node)
_cursor = prev;
BaseCompiler_nodeRemoved(this, node);
return node;
}
void Compiler::removeNodes(Node* first, Node* last) {
if (first == last) {
removeNode(first);
return;
}
Node* prev = first->_prev;
Node* next = last->_next;
if (_firstNode == first)
_firstNode = next;
else
prev->_next = next;
if (_lastNode == last)
_lastNode = prev;
else
next->_prev = prev;
Node* node = first;
for (;;) {
Node* next = node->getNext();
ASMJIT_ASSERT(next != NULL);
node->_prev = NULL;
node->_next = NULL;
if (_cursor == node)
_cursor = prev;
BaseCompiler_nodeRemoved(this, node);
if (node == last)
break;
node = next;
}
}
// ============================================================================
// [asmjit::Compiler - Align]
// ============================================================================
AlignNode* Compiler::newAlign(uint32_t mode, uint32_t offset) {
AlignNode* node = newNode<AlignNode>(mode, offset);
if (node == NULL)
goto _NoMemory;
return node;
_NoMemory:
setError(kErrorNoHeapMemory);
return NULL;
}
AlignNode* Compiler::addAlign(uint32_t mode, uint32_t offset) {
AlignNode* node = newAlign(mode, offset);
if (node == NULL)
return NULL;
return static_cast<AlignNode*>(addNode(node));
}
// ============================================================================
// [asmjit::Compiler - Target]
// ============================================================================
TargetNode* Compiler::newTarget() {
TargetNode* node = newNode<TargetNode>(
OperandUtil::makeLabelId(static_cast<uint32_t>(_targetList.getLength())));
if (node == NULL || _targetList.append(node) != kErrorOk)
goto _NoMemory;
return node;
_NoMemory:
setError(kErrorNoHeapMemory);
return NULL;
}
TargetNode* Compiler::addTarget() {
TargetNode* node = newTarget();
if (node == NULL)
return NULL;
return static_cast<TargetNode*>(addNode(node));
}
// ============================================================================
// [asmjit::Compiler - Label]
// ============================================================================
Error Compiler::_newLabel(Label* dst) {
dst->_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, kInvalidValue);
dst->_init_packed_d2_d3(0, 0);
TargetNode* node = newTarget();
if (node == NULL)
goto _NoMemory;
dst->_label.id = node->getLabelId();
return kErrorOk;
_NoMemory:
return setError(kErrorNoHeapMemory);
}
Error Compiler::bind(const Label& label) {
uint32_t index = label.getId();
ASMJIT_ASSERT(index < _targetList.getLength());
addNode(_targetList[index]);
return kErrorOk;
}
// ============================================================================
// [asmjit::Compiler - Embed]
// ============================================================================
EmbedNode* Compiler::newEmbed(const void* data, uint32_t size) {
EmbedNode* node;
if (size > EmbedNode::kInlineBufferSize) {
void* clonedData = _stringZone.alloc(size);
if (clonedData == NULL)
goto _NoMemory;
if (data != NULL)
::memcpy(clonedData, data, size);
data = clonedData;
}
node = newNode<EmbedNode>(const_cast<void*>(data), size);
if (node == NULL)
goto _NoMemory;
return node;
_NoMemory:
setError(kErrorNoHeapMemory);
return NULL;
}
EmbedNode* Compiler::addEmbed(const void* data, uint32_t size) {
EmbedNode* node = newEmbed(data, size);
if (node == NULL)
return node;
return static_cast<EmbedNode*>(addNode(node));
}
// ============================================================================
// [asmjit::Compiler - Comment]
// ============================================================================
CommentNode* Compiler::newComment(const char* str) {
CommentNode* node;
if (str != NULL && str[0]) {
str = _stringZone.sdup(str);
if (str == NULL)
goto _NoMemory;
}
node = newNode<CommentNode>(str);
if (node == NULL)
goto _NoMemory;
return node;
_NoMemory:
setError(kErrorNoHeapMemory);
return NULL;
}
CommentNode* Compiler::addComment(const char* str) {
CommentNode* node = newComment(str);
if (node == NULL)
return NULL;
return static_cast<CommentNode*>(addNode(node));
}
CommentNode* Compiler::comment(const char* fmt, ...) {
char buf[256];
char* p = buf;
if (fmt) {
*p++ = ';';
*p++ = ' ';
va_list ap;
va_start(ap, fmt);
p += vsnprintf(p, 254, fmt, ap);
va_end(ap);
}
p[0] = '\n';
p[1] = '\0';
return addComment(fmt);
}
// ============================================================================
// [asmjit::Compiler - Hint]
// ============================================================================
HintNode* Compiler::newHint(Var& var, uint32_t hint, uint32_t value) {
if (var.getId() == kInvalidValue)
return NULL;
VarData* vd = getVd(var);
HintNode* node = newNode<HintNode>(vd, hint, value);
if (node == NULL)
goto _NoMemory;
return node;
_NoMemory:
setError(kErrorNoHeapMemory);
return NULL;
}
HintNode* Compiler::addHint(Var& var, uint32_t hint, uint32_t value) {
if (var.getId() == kInvalidValue)
return NULL;
HintNode* node = newHint(var, hint, value);
if (node == NULL)
return NULL;
return static_cast<HintNode*>(addNode(node));
}
// ============================================================================
// [asmjit::Compiler - Vars]
// ============================================================================
VarData* Compiler::_newVd(uint32_t type, uint32_t size, uint32_t c, const char* name) {
VarData* vd = reinterpret_cast<VarData*>(_varZone.alloc(sizeof(VarData)));
if (vd == NULL)
goto _NoMemory;
vd->_name = noName;
vd->_id = OperandUtil::makeVarId(static_cast<uint32_t>(_varList.getLength()));
vd->_contextId = kInvalidValue;
if (name != NULL && name[0] != '\0') {
vd->_name = _stringZone.sdup(name);
}
vd->_type = static_cast<uint8_t>(type);
vd->_class = static_cast<uint8_t>(c);
vd->_flags = 0;
vd->_priority = 10;
vd->_state = kVarStateNone;
vd->_regIndex = kInvalidReg;
vd->_isStack = false;
vd->_isMemArg = false;
vd->_isCalculated = false;
vd->_saveOnUnuse = false;
vd->_modified = false;
vd->_reserved0 = 0;
vd->_alignment = static_cast<uint8_t>(IntUtil::iMin<uint32_t>(size, 64));
vd->_size = size;
vd->_homeMask = 0;
vd->_memOffset = 0;
vd->_memCell = NULL;
vd->rReadCount = 0;
vd->rWriteCount = 0;
vd->mReadCount = 0;
vd->mWriteCount = 0;
vd->_va = NULL;
if (_varList.append(vd) != kErrorOk)
goto _NoMemory;
return vd;
_NoMemory:
setError(kErrorNoHeapMemory);
return NULL;
}
void Compiler::alloc(Var& var) {
addHint(var, kVarHintAlloc, kInvalidValue);
}
void Compiler::alloc(Var& var, uint32_t regIndex) {
addHint(var, kVarHintAlloc, regIndex);
}
void Compiler::alloc(Var& var, const Reg& reg) {
addHint(var, kVarHintAlloc, reg.getRegIndex());
}
void Compiler::save(Var& var) {
addHint(var, kVarHintSave, kInvalidValue);
}
void Compiler::spill(Var& var) {
addHint(var, kVarHintSpill, kInvalidValue);
}
void Compiler::unuse(Var& var) {
addHint(var, kVarHintUnuse, kInvalidValue);
}
uint32_t Compiler::getPriority(Var& var) const {
if (var.getId() == kInvalidValue)
return kInvalidValue;
VarData* vd = getVdById(var.getId());
return vd->getPriority();
}
void Compiler::setPriority(Var& var, uint32_t priority) {
if (var.getId() == kInvalidValue)
return;
if (priority > 255)
priority = 255;
VarData* vd = getVdById(var.getId());
vd->_priority = static_cast<uint8_t>(priority);
}
bool Compiler::getSaveOnUnuse(Var& var) const {
if (var.getId() == kInvalidValue)
return false;
VarData* vd = getVdById(var.getId());
return static_cast<bool>(vd->_saveOnUnuse);
}
void Compiler::setSaveOnUnuse(Var& var, bool value) {
if (var.getId() == kInvalidValue)
return;
VarData* vd = getVdById(var.getId());
vd->_saveOnUnuse = value;
}
void Compiler::rename(Var& var, const char* name) {
if (var.getId() == kInvalidValue)
return;
VarData* vd = getVdById(var.getId());
vd->_name = noName;
if (name != NULL && name[0] != '\0') {
vd->_name = _stringZone.sdup(name);
}
}
// ============================================================================
// [asmjit::Compiler - Assembler]
// ============================================================================
Assembler* Compiler::getAssembler() {
Assembler* a = _assembler;
if (a != NULL) {
a->reset(false);
}
else {
a = _newAssembler();
_assembler = a;
}
#if !defined(ASMJIT_DISABLE_LOGGER)
Logger* logger = _logger;
if (logger != NULL)
a->setLogger(logger);
#endif // !ASMJIT_DISABLE_LOGGER
a->setBaseAddress(_baseAddress);
a->setFeatures(_features);
return a;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_COMPILER_H
#define _ASMJIT_BASE_COMPILER_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies - AsmJit]
#include "../base/assembler.h"
#include "../base/codegen.h"
#include "../base/constpool.h"
#include "../base/containers.h"
#include "../base/error.h"
#include "../base/intutil.h"
#include "../base/operand.h"
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct Compiler;
struct VarAttr;
struct VarData;
struct VarMap;
struct VarState;
struct Node;
struct EndNode;
struct InstNode;
struct JumpNode;
// ============================================================================
// [asmjit::ConstScope]
// ============================================================================
//! \addtogroup asmjit_base_compiler
//! \{
//! Scope of the constant.
ASMJIT_ENUM(ConstScope) {
//! Local constant, always embedded right after the current function.
kConstScopeLocal = 0,
//! Global constant, embedded at the end of the currently compiled code.
kConstScopeGlobal = 1
};
// ============================================================================
// [asmjit::VarType]
// ============================================================================
ASMJIT_ENUM(VarType) {
//! Variable is 8-bit signed integer.
kVarTypeInt8 = 0,
//! Variable is 8-bit unsigned integer.
kVarTypeUInt8 = 1,
//! Variable is 16-bit signed integer.
kVarTypeInt16 = 2,
//! Variable is 16-bit unsigned integer.
kVarTypeUInt16 = 3,
//! Variable is 32-bit signed integer.
kVarTypeInt32 = 4,
//! Variable is 32-bit unsigned integer.
kVarTypeUInt32 = 5,
//! Variable is 64-bit signed integer.
kVarTypeInt64 = 6,
//! Variable is 64-bit unsigned integer.
kVarTypeUInt64 = 7,
//! Variable is target `intptr_t`, not compatible with host `intptr_t`.
kVarTypeIntPtr = 8,
//! Variable is target `uintptr_t`, not compatible with host `uintptr_t`.
kVarTypeUIntPtr = 9,
//! Variable is 32-bit floating point (single precision).
kVarTypeFp32 = 10,
//! Variable is 64-bit floating point (double precision).
kVarTypeFp64 = 11,
//! \internal
_kVarTypeIntStart = kVarTypeInt8,
//! \internal
_kVarTypeIntEnd = kVarTypeUIntPtr,
//! \internal
_kVarTypeFpStart = kVarTypeFp32,
//! \internal
_kVarTypeFpEnd = kVarTypeFp64
};
// ============================================================================
// [asmjit::VarFlags]
// ============================================================================
//! \internal
//!
//! X86/X64 variable flags.
ASMJIT_ENUM(VarFlags) {
//! Variable contains single-precision floating-point(s).
kVarFlagSp = 0x10,
//! Variable contains double-precision floating-point(s).
kVarFlagDp = 0x20,
//! Variable is packed, i.e. packed floats, doubles, ...
kVarFlagPacked = 0x40
};
// ============================================================================
// [asmjit::VarAttrFlags]
// ============================================================================
//! Variable attribute flags.
ASMJIT_ENUM(VarAttrFlags) {
//! Variable is accessed through register on input.
kVarAttrInReg = 0x00000001,
//! Variable is accessed through register on output.
kVarAttrOutReg = 0x00000002,
//! Variable is accessed through register on input & output.
kVarAttrInOutReg = 0x00000003,
//! Variable is accessed through memory on input.
kVarAttrInMem = 0x00000004,
//! Variable is accessed through memory on output.
kVarAttrOutMem = 0x00000008,
//! Variable is accessed through memory on input & output.
kVarAttrInOutMem = 0x0000000C,
//! Register allocator can decide if input will be in register or memory.
kVarAttrInDecide = 0x00000010,
//! Register allocator can decide if output will be in register or memory.
kVarAttrOutDecide = 0x00000020,
//! Register allocator can decide if in/out will be in register or memory.
kVarAttrInOutDecide = 0x00000030,
//! Variable is converted to other type/class on the input.
kVarAttrInConv = 0x00000040,
//! Variable is converted from other type/class on the output.
kVarAttrOutConv = 0x00000080,
//! Combination of `kVarAttrInConv` and `kVarAttrOutConv`.
kVarAttrInOutConv = 0x000000C0,
//! Variable is a function call operand.
kVarAttrInCall = 0x00000100,
//! Variable is a function argument passed in register.
kVarAttrInArg = 0x00000200,
//! Variable is a function return value passed in register.
kVarAttrOutRet = 0x00000400,
//! Variable should be spilled.
kVarAttrSpill = 0x00000800,
//! Variable should be unused at the end of the instruction/node.
kVarAttrUnuse = 0x00001000,
//! \internal
//!
//! All in-flags.
kVarAttrInAll =
kVarAttrInReg |
kVarAttrInMem |
kVarAttrInDecide |
kVarAttrInCall |
kVarAttrInArg,
//! \internal
//!
//! All out-flags.
kVarAttrOutAll =
kVarAttrOutReg |
kVarAttrOutMem |
kVarAttrOutDecide |
kVarAttrOutRet,
//! Variable is already allocated on the input.
kVarAttrAllocInDone = 0x00400000,
//! Variable is already allocated on the output.
kVarAttrAllocOutDone = 0x00800000
};
// ============================================================================
// [asmjit::VarHint]
// ============================================================================
//! Variable hint (used by `Compiler)`.
//!
//! \sa Compiler.
ASMJIT_ENUM(VarHint) {
//! Alloc variable.
kVarHintAlloc = 0,
//! Spill variable.
kVarHintSpill = 1,
//! Save variable if modified.
kVarHintSave = 2,
//! Save variable if modified and mark it as unused.
kVarHintSaveAndUnuse = 3,
//! Mark variable as unused.
kVarHintUnuse = 4
};
// ============================================================================
// [asmjit::kVarState]
// ============================================================================
// TODO: Rename `kVarState` or `VarState`.
//! State of variable.
//!
//! \note Variable states are used only during register allocation.
ASMJIT_ENUM(kVarState) {
//! Variable is currently not used.
kVarStateNone = 0,
//! Variable is currently allocated in register.
kVarStateReg = 1,
//! Variable is currently allocated in memory (or has been spilled).
kVarStateMem = 2
};
// ============================================================================
// [asmjit::FuncConv]
// ============================================================================
//! Function calling convention.
//!
//! For a platform specific calling conventions, see:
//! - `X86FuncConv` - X86/X64 calling conventions.
ASMJIT_ENUM(FuncConv) {
//! Calling convention is invalid (can't be used).
kFuncConvNone = 0,
#if defined(ASMJIT_DOCGEN)
//! Default calling convention for current platform / operating system.
kFuncConvHost = DependsOnHost,
//! Default C calling convention based on current compiler's settings.
kFuncConvHostCDecl = DependsOnHost,
//! Compatibility for `__stdcall` calling convention.
//!
//! \note This enumeration is always set to a value which is compatible with
//! current compilers __stdcall calling convention. In 64-bit mode the value
//! is compatible with `kX86FuncConvW64` or `kX86FuncConvU64`.
kFuncConvHostStdCall = DependsOnHost,
//! Compatibility for `__fastcall` calling convention.
//!
//! \note This enumeration is always set to a value which is compatible with
//! current compilers `__fastcall` calling convention. In 64-bit mode the value
//! is compatible with `kX86FuncConvW64` or `kX86FuncConvU64`.
kFuncConvHostFastCall = DependsOnHost
#endif // ASMJIT_DOCGEN
};
// ============================================================================
// [asmjit::FuncHint]
// ============================================================================
//! Function hints.
//!
//! For a platform specific calling conventions, see:
//! - `X86FuncHint` - X86/X64 function hints.
ASMJIT_ENUM(FuncHint) {
//! Make a naked function (default true).
//!
//! Naked function is function without using standard prolog/epilog sequence).
//!
//! X86/X64 Specific
//! ----------------
//!
//! Standard prolog sequence is:
//!
//! ~~~
//! push zbp
//! mov zsp, zbp
//! sub zsp, StackAdjustment
//! ~~~
//!
//! which is an equivalent to:
//!
//! ~~~
//! enter StackAdjustment, 0
//! ~~~
//!
//! Standard epilog sequence is:
//!
//! ~~~
//! mov zsp, zbp
//! pop zbp
//! ~~~
//!
//! which is an equavalent to:
//!
//! ~~~
//! leave
//! ~~~
//!
//! Naked functions can omit the prolog/epilog sequence. The advantage of
//! doing such modification is that EBP/RBP register can be used by the
//! register allocator which can result in less spills/allocs.
kFuncHintNaked = 0,
//! Generate compact function prolog/epilog if possible.
//!
//! X86/X64 Specific
//! ----------------
//!
//! Use shorter, but possible slower prolog/epilog sequence to save/restore
//! registers.
kFuncHintCompact = 1
};
// ============================================================================
// [asmjit::FuncFlags]
// ============================================================================
//! Function flags.
//!
//! For a platform specific calling conventions, see:
//! - `X86FuncFlags` - X86/X64 function flags.
ASMJIT_ENUM(FuncFlags) {
//! Whether the function is using naked (minimal) prolog / epilog.
kFuncFlagIsNaked = 0x00000001,
//! Whether an another function is called from this function.
kFuncFlagIsCaller = 0x00000002,
//! Whether the stack is not aligned to the required stack alignment,
//! thus it has to be aligned manually.
kFuncFlagIsStackMisaligned = 0x00000004,
//! Whether the stack pointer is adjusted by the stack size needed
//! to save registers and function variables.
//!
//! X86/X64 Specific
//! ----------------
//!
//! Stack pointer (ESP/RSP) is adjusted by 'sub' instruction in prolog and by
//! 'add' instruction in epilog (only if function is not naked). If function
//! needs to perform manual stack alignment more instructions are used to
//! adjust the stack (like "and zsp, -Alignment").
kFuncFlagIsStackAdjusted = 0x00000008,
//! Whether the function is finished using `Compiler::endFunc()`.
kFuncFlagIsFinished = 0x80000000
};
// ============================================================================
// [asmjit::FuncDir]
// ============================================================================
//! Function arguments direction.
ASMJIT_ENUM(FuncDir) {
//! Arguments are passed left to right.
//!
//! This arguments direction is unusual in C, however it's used in Pascal.
kFuncDirLtr = 0,
//! Arguments are passed right ro left
//!
//! This is the default argument direction in C.
kFuncDirRtl = 1
};
// ============================================================================
// [asmjit::FuncArgIndex]
// ============================================================================
//! Function argument index (lo/hi).
ASMJIT_ENUM(FuncArgIndex) {
//! Maxumum number of function arguments supported by AsmJit.
kFuncArgCount = 16,
//! Extended maximum number of arguments (used internally).
kFuncArgCountLoHi = kFuncArgCount * 2,
//! Index to the LO part of function argument (default).
//!
//! This value is typically omitted and added only if there is HI argument
//! accessed.
kFuncArgLo = 0,
//! Index to the HI part of function argument.
//!
//! HI part of function argument depends on target architecture. On x86 it's
//! typically used to transfer 64-bit integers (they form a pair of 32-bit
//! integers).
kFuncArgHi = kFuncArgCount
};
// ============================================================================
// [asmjit::FuncRet]
// ============================================================================
//! Function return value (lo/hi) specification.
ASMJIT_ENUM(FuncRet) {
//! Index to the LO part of function return value.
kFuncRetLo = 0,
//! Index to the HI part of function return value.
kFuncRetHi = 1
};
// ============================================================================
// [asmjit::kFuncStackInvalid]
// ============================================================================
enum {
//! Invalid stack offset in function or function parameter.
kFuncStackInvalid = -1
};
// ============================================================================
// [asmjit::NodeType]
// ============================================================================
//! Type of node, see \ref Node.
ASMJIT_ENUM(NodeType) {
//! Invalid node (internal, can't be used).
kNodeTypeNone = 0,
//! Node is an .align directive, see \ref AlignNode.
kNodeTypeAlign,
//! Node is an embedded data, see \ref EmbedNode.
kNodeTypeEmbed,
//! Node is a comment, see \ref CommentNode.
kNodeTypeComment,
//! Node is a variable hint (alloc, spill, use, unuse), see \ref HintNode.
kNodeTypeHint,
//! Node is a label, see \ref TargetNode.
kNodeTypeTarget,
//! Node is an instruction, see \ref InstNode.
kNodeTypeInst,
//! Node is a function declaration, see \ref FuncNode.
kNodeTypeFunc,
//! Node is an end of the function, see \ref EndNode.
kNodeTypeEnd,
//! Node is a return, see \ref RetNode.
kNodeTypeRet,
//! Node is a function call, see \ref CallNode.
kNodeTypeCall,
//! Node is a function call argument moved on stack, see \ref SArgNode.
kNodeTypeSArg
};
// ============================================================================
// [asmjit::NodeFlags]
// ============================================================================
ASMJIT_ENUM(NodeFlags) {
//! Whether the node has been translated, thus contains only registers.
kNodeFlagIsTranslated = 0x0001,
//! Whether the node was scheduled - possibly reordered, but basically this
//! is a mark that is set by scheduler after the node has been visited.
kNodeFlagIsScheduled = 0x0002,
//! Whether the node is informative only and can be safely removed.
kNodeFlagIsInformative = 0x0004,
//! Whether the `InstNode` is a jump.
kNodeFlagIsJmp = 0x0008,
//! Whether the `InstNode` is a conditional jump.
kNodeFlagIsJcc = 0x0010,
//! Whether the `InstNode` is an unconditinal jump or conditional
//! jump that is likely to be taken.
kNodeFlagIsTaken = 0x0020,
//! Whether the `Node` will return from a function.
//!
//! This flag is used by both `EndNode` and `RetNode`.
kNodeFlagIsRet = 0x0040,
//! Whether the instruction is special.
kNodeFlagIsSpecial = 0x0080,
//! Whether the instruction is an FPU instruction.
kNodeFlagIsFp = 0x0100
};
// ============================================================================
// [asmjit::MemCell]
// ============================================================================
struct MemCell {
ASMJIT_NO_COPY(MemCell)
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get cell offset.
ASMJIT_INLINE int32_t getOffset() const { return _offset; }
//! Set cell offset.
ASMJIT_INLINE void setOffset(int32_t offset) { _offset = offset; }
//! Get cell size.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! Set cell size.
ASMJIT_INLINE void setSize(uint32_t size) { _size = size; }
//! Get cell alignment.
ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; }
//! Set cell alignment.
ASMJIT_INLINE void setAlignment(uint32_t alignment) { _alignment = alignment; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Next active cell.
MemCell* _next;
//! Offset, relative to base-offset.
int32_t _offset;
//! Size.
uint32_t _size;
//! Alignment.
uint32_t _alignment;
};
// ============================================================================
// [asmjit::Var]
// ============================================================================
//! Base class for all variables.
struct Var : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE Var() : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeVar, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(kInvalidValue, kInvalidValue);
}
ASMJIT_INLINE Var(const Var& other) : Operand(other) {}
explicit ASMJIT_INLINE Var(const _NoInit&) : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Var Specific]
// --------------------------------------------------------------------------
//! Clone `Var` operand.
ASMJIT_INLINE Var clone() const {
return Var(*this);
}
//! Reset Var operand.
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeVar, 0, kInvalidReg, kInvalidReg, kInvalidValue);
_init_packed_d2_d3(kInvalidValue, kInvalidValue);
}
//! Get whether the variable has been initialized by `Compiler`.
ASMJIT_INLINE bool isInitialized() const {
return _vreg.id != kInvalidValue;
}
//! Get variable type.
ASMJIT_INLINE uint32_t getVarType() const {
return _vreg.vType;
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE Var& operator=(const Var& other) { _copy(other); return *this; }
ASMJIT_INLINE bool operator==(const Var& other) const { return _packed[0] == other._packed[0]; }
ASMJIT_INLINE bool operator!=(const Var& other) const { return !operator==(other); }
};
// ============================================================================
// [asmjit::VarBits]
// ============================================================================
//! Bit-array used by variable-liveness analysis.
struct VarBits {
// --------------------------------------------------------------------------
// [Enums]
// --------------------------------------------------------------------------
enum {
kEntitySize = static_cast<int>(sizeof(uintptr_t)),
kEntityBits = kEntitySize * 8
};
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uintptr_t getBit(uint32_t index) const {
return (data[index / kEntityBits] >> (index % kEntityBits)) & 1;
}
ASMJIT_INLINE void setBit(uint32_t index) {
data[index / kEntityBits] |= static_cast<uintptr_t>(1) << (index % kEntityBits);
}
ASMJIT_INLINE void delBit(uint32_t index) {
data[index / kEntityBits] &= ~(static_cast<uintptr_t>(1) << (index % kEntityBits));
}
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool copyBits(const VarBits* s0, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool addBits(const VarBits* s0, uint32_t len) {
return addBits(this, s0, len);
}
ASMJIT_INLINE bool addBits(const VarBits* s0, const VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] | s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool andBits(const VarBits* s1, uint32_t len) {
return andBits(this, s1, len);
}
ASMJIT_INLINE bool andBits(const VarBits* s0, const VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] & s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool delBits(const VarBits* s1, uint32_t len) {
return delBits(this, s1, len);
}
ASMJIT_INLINE bool delBits(const VarBits* s0, const VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] & ~s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool _addBitsDelSource(VarBits* s1, uint32_t len) {
return _addBitsDelSource(this, s1, len);
}
ASMJIT_INLINE bool _addBitsDelSource(const VarBits* s0, VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t a = s0->data[i];
uintptr_t b = s1->data[i];
this->data[i] = a | b;
b &= ~a;
s1->data[i] = b;
r |= b;
}
return r != 0;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uintptr_t data[1];
};
// ============================================================================
// [asmjit::VarData]
// ============================================================================
//! Base variable data.
struct VarData {
// --------------------------------------------------------------------------
// [Accessors - Base]
// --------------------------------------------------------------------------
//! Get variable name.
ASMJIT_INLINE const char* getName() const {
return _name;
}
//! Get variable id.
ASMJIT_INLINE uint32_t getId() const {
return _id;
}
//! Get variable type.
ASMJIT_INLINE uint32_t getType() const {
return _type;
}
//! Get variable class.
ASMJIT_INLINE uint32_t getClass() const {
return _class;
}
// --------------------------------------------------------------------------
// [Accessors - ContextId]
// --------------------------------------------------------------------------
//! Get whether the variable has context id.
ASMJIT_INLINE bool hasContextId() const {
return _contextId != kInvalidValue;
}
//! Get context variable id (used only by `Context)`.
ASMJIT_INLINE uint32_t getContextId() const {
return _contextId;
}
//! Set context variable id (used only by `Context)`.
ASMJIT_INLINE void setContextId(uint32_t contextId) {
_contextId = contextId;
}
//! Reset context variable id (used only by `Context)`.
ASMJIT_INLINE void resetContextId() {
_contextId = kInvalidValue;
}
// --------------------------------------------------------------------------
// [Accessors - Priority]
// --------------------------------------------------------------------------
//! Get variable priority, used by compiler to decide which variable to spill.
ASMJIT_INLINE uint32_t getPriority() const {
return _priority;
}
//! Set variable priority.
ASMJIT_INLINE void setPriority(uint32_t priority) {
ASMJIT_ASSERT(priority <= 0xFF);
_priority = static_cast<uint8_t>(priority);
}
// --------------------------------------------------------------------------
// [Accessors - State]
// --------------------------------------------------------------------------
//! Get variable state, only used by `Context`.
ASMJIT_INLINE uint32_t getState() const {
return _state;
}
//! Set variable state, only used by `Context`.
ASMJIT_INLINE void setState(uint32_t state) {
ASMJIT_ASSERT(state <= 0xFF);
_state = static_cast<uint8_t>(state);
}
// --------------------------------------------------------------------------
// [Accessors - RegIndex]
// --------------------------------------------------------------------------
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const {
return _regIndex;
}
//! Set register index.
ASMJIT_INLINE void setRegIndex(uint32_t regIndex) {
ASMJIT_ASSERT(regIndex <= 0xFF);
_regIndex = static_cast<uint8_t>(regIndex);
}
//! Reset register index.
ASMJIT_INLINE void resetRegIndex() {
_regIndex = static_cast<uint8_t>(kInvalidReg);
}
// --------------------------------------------------------------------------
// [Accessors - HomeIndex/Mask]
// --------------------------------------------------------------------------
//! Get home registers mask.
ASMJIT_INLINE uint32_t getHomeMask() const {
return _homeMask;
}
//! Add a home register index to the home registers mask.
ASMJIT_INLINE void addHomeIndex(uint32_t regIndex) {
_homeMask |= IntUtil::mask(regIndex);
}
// --------------------------------------------------------------------------
// [Accessors - Flags]
// --------------------------------------------------------------------------
//! Get variable flags.
ASMJIT_INLINE uint32_t getFlags() const {
return _flags;
}
//! Get whether the VarData is only memory allocated on the stack.
ASMJIT_INLINE bool isStack() const { return static_cast<bool>(_isStack); }
//! Get whether the variable is a function argument passed through memory.
ASMJIT_INLINE bool isMemArg() const { return static_cast<bool>(_isMemArg); }
//! Get variable content can be calculated by a simple instruction.
ASMJIT_INLINE bool isCalculated() const { return static_cast<bool>(_isCalculated); }
//! Get whether to save variable when it's unused (spill).
ASMJIT_INLINE bool saveOnUnuse() const { return static_cast<bool>(_saveOnUnuse); }
//! Get whether the variable was changed.
ASMJIT_INLINE bool isModified() const { return static_cast<bool>(_modified); }
//! Set whether the variable was changed.
ASMJIT_INLINE void setModified(bool modified) { _modified = modified; }
//! Get variable alignment.
ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; }
//! Get variable size.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! Get home memory offset.
ASMJIT_INLINE int32_t getMemOffset() const { return _memOffset; }
//! Set home memory offset.
ASMJIT_INLINE void setMemOffset(int32_t offset) { _memOffset = offset; }
//! Get home memory cell.
ASMJIT_INLINE MemCell* getMemCell() const { return _memCell; }
//! Set home memory cell.
ASMJIT_INLINE void setMemCell(MemCell* cell) { _memCell = cell; }
// --------------------------------------------------------------------------
// [Accessors - Temporary Usage]
// --------------------------------------------------------------------------
//! Get temporary VarAttr.
ASMJIT_INLINE VarAttr* getVa() const { return _va; }
//! Set temporary VarAttr.
ASMJIT_INLINE void setVa(VarAttr* va) { _va = va; }
//! Reset temporary VarAttr.
ASMJIT_INLINE void resetVa() { _va = NULL; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Variable name.
const char* _name;
//! Variable id.
uint32_t _id;
//! Context variable id, used by `Context` only, initially `kInvalidValue`.
uint32_t _contextId;
//! Variable type.
uint8_t _type;
//! Variable class.
uint8_t _class;
//! Variable flags.
uint8_t _flags;
//! Variable priority.
uint8_t _priority;
//! Variable state (connected with actual `VarState)`.
uint8_t _state;
//! Actual register index (only used by `Context)`, during translate.
uint8_t _regIndex;
//! Whether the variable is only used as memory allocated on the stack.
uint8_t _isStack : 1;
//! Whether the variable is a function argument passed through memory.
uint8_t _isMemArg : 1;
//! Whether variable content can be calculated by a simple instruction.
//!
//! This is used mainly by MMX and SSE2 code. This flag indicates that
//! register allocator should never reserve memory for this variable, because
//! the content can be generated by a single instruction (for example PXOR).
uint8_t _isCalculated : 1;
//! Save on unuse (at end of the variable scope).
uint8_t _saveOnUnuse : 1;
//! Whether variable was changed (connected with actual `VarState)`.
uint8_t _modified : 1;
//! \internal
uint8_t _reserved0 : 3;
//! Variable natural alignment.
uint8_t _alignment;
//! Variable size.
uint32_t _size;
//! Mask of all registers variable has been allocated to.
uint32_t _homeMask;
//! Home memory offset.
int32_t _memOffset;
//! Home memory cell, used by `Context` (initially NULL).
MemCell* _memCell;
//! Register read access statistics.
uint32_t rReadCount;
//! Register write access statistics.
uint32_t rWriteCount;
//! Memory read statistics.
uint32_t mReadCount;
//! Memory write statistics.
uint32_t mWriteCount;
// --------------------------------------------------------------------------
// [Members - Temporary Usage]
// --------------------------------------------------------------------------
// These variables are only used during register allocation. They are
// initialized by init() phase and reset by cleanup() phase.
union {
//! Temporary link to VarAttr* used by the `Context` used in
//! various phases, but always set back to NULL when finished.
//!
//! This temporary data is designed to be used by algorithms that need to
//! store some data into variables themselves during compilation. But it's
//! expected that after variable is compiled & translated the data is set
//! back to zero/null. Initial value is NULL.
VarAttr* _va;
//! \internal
//!
//! Same as `_va` just provided as `uintptr_t`.
uintptr_t _vaUInt;
};
};
// ============================================================================
// [asmjit::VarAttr]
// ============================================================================
struct VarAttr {
// --------------------------------------------------------------------------
// [Setup]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setup(VarData* vd, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) {
_vd = vd;
_flags = flags;
_varCount = 0;
_inRegIndex = kInvalidReg;
_outRegIndex = kInvalidReg;
_reserved = 0;
_inRegs = inRegs;
_allocableRegs = allocableRegs;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get VarData.
ASMJIT_INLINE VarData* getVd() const { return _vd; }
//! Set VarData.
ASMJIT_INLINE void setVd(VarData* vd) { _vd = vd; }
//! Get flags.
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
//! Set flags.
ASMJIT_INLINE void setFlags(uint32_t flags) { _flags = flags; }
//! Get whether `flag` is on.
ASMJIT_INLINE bool hasFlag(uint32_t flag) { return (_flags & flag) != 0; }
//! Add `flags`.
ASMJIT_INLINE void orFlags(uint32_t flags) { _flags |= flags; }
//! Mask `flags`.
ASMJIT_INLINE void andFlags(uint32_t flags) { _flags &= flags; }
//! Clear `flags`.
ASMJIT_INLINE void andNotFlags(uint32_t flags) { _flags &= ~flags; }
//! Get how many times the variable is used by the instruction/node.
ASMJIT_INLINE uint32_t getVarCount() const { return _varCount; }
//! Set how many times the variable is used by the instruction/node.
ASMJIT_INLINE void setVarCount(uint32_t count) { _varCount = static_cast<uint8_t>(count); }
//! Add how many times the variable is used by the instruction/node.
ASMJIT_INLINE void addVarCount(uint32_t count = 1) { _varCount += static_cast<uint8_t>(count); }
//! Get whether the variable has to be allocated in a specific input register.
ASMJIT_INLINE uint32_t hasInRegIndex() const { return _inRegIndex != kInvalidReg; }
//! Get the input register index or `kInvalidReg`.
ASMJIT_INLINE uint32_t getInRegIndex() const { return _inRegIndex; }
//! Set the input register index.
ASMJIT_INLINE void setInRegIndex(uint32_t index) { _inRegIndex = static_cast<uint8_t>(index); }
//! Reset the input register index.
ASMJIT_INLINE void resetInRegIndex() { _inRegIndex = kInvalidReg; }
//! Get whether the variable has to be allocated in a specific output register.
ASMJIT_INLINE uint32_t hasOutRegIndex() const { return _outRegIndex != kInvalidReg; }
//! Get the output register index or `kInvalidReg`.
ASMJIT_INLINE uint32_t getOutRegIndex() const { return _outRegIndex; }
//! Set the output register index.
ASMJIT_INLINE void setOutRegIndex(uint32_t index) { _outRegIndex = static_cast<uint8_t>(index); }
//! Reset the output register index.
ASMJIT_INLINE void resetOutRegIndex() { _outRegIndex = kInvalidReg; }
//! Get whether the mandatory input registers are in used.
ASMJIT_INLINE bool hasInRegs() const { return _inRegs != 0; }
//! Get mandatory input registers (mask).
ASMJIT_INLINE uint32_t getInRegs() const { return _inRegs; }
//! Set mandatory input registers (mask).
ASMJIT_INLINE void setInRegs(uint32_t mask) { _inRegs = mask; }
//! Add mandatory input registers (mask).
ASMJIT_INLINE void addInRegs(uint32_t mask) { _inRegs |= mask; }
//! And mandatory input registers (mask).
ASMJIT_INLINE void andInRegs(uint32_t mask) { _inRegs &= mask; }
//! Clear mandatory input registers (mask).
ASMJIT_INLINE void delInRegs(uint32_t mask) { _inRegs &= ~mask; }
//! Get allocable input registers (mask).
ASMJIT_INLINE uint32_t getAllocableRegs() const { return _allocableRegs; }
//! Set allocable input registers (mask).
ASMJIT_INLINE void setAllocableRegs(uint32_t mask) { _allocableRegs = mask; }
//! Add allocable input registers (mask).
ASMJIT_INLINE void addAllocableRegs(uint32_t mask) { _allocableRegs |= mask; }
//! And allocable input registers (mask).
ASMJIT_INLINE void andAllocableRegs(uint32_t mask) { _allocableRegs &= mask; }
//! Clear allocable input registers (mask).
ASMJIT_INLINE void delAllocableRegs(uint32_t mask) { _allocableRegs &= ~mask; }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE VarAttr& operator=(const VarAttr& other) {
::memcpy(this, &other, sizeof(VarAttr));
return *this;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
VarData* _vd;
//! Flags.
uint32_t _flags;
union {
struct {
//! How many times the variable is used by the instruction/node.
uint8_t _varCount;
//! Input register index or `kInvalidReg` if it's not given.
//!
//! Even if the input register index is not given (i.e. it may by any
//! register), register allocator should assign an index that will be
//! used to persist a variable into this specific index. It's helpful
//! in situations where one variable has to be allocated in multiple
//! registers to determine the register which will be persistent.
uint8_t _inRegIndex;
//! Output register index or `kInvalidReg` if it's not given.
//!
//! Typically `kInvalidReg` if variable is only used on input.
uint8_t _outRegIndex;
//! \internal
uint8_t _reserved;
};
//! \internal
//!
//! Packed data #0.
uint32_t _packed;
};
//! Mandatory input registers.
//!
//! Mandatory input registers are required by the instruction even if
//! there are duplicates. This schema allows us to allocate one variable
//! in one or more register when needed. Required mostly by instructions
//! that have implicit register operands (imul, cpuid, ...) and function
//! call.
uint32_t _inRegs;
//! Allocable input registers.
//!
//! Optional input registers is a mask of all allocable registers for a given
//! variable where we have to pick one of them. This mask is usually not used
//! when _inRegs is set. If both masks are used then the register
//! allocator tries first to find an intersection between these and allocates
//! an extra slot if not found.
uint32_t _allocableRegs;
};
// ============================================================================
// [asmjit::VarMap]
// ============================================================================
//! Variables' map related to a single node (instruction / other node).
struct VarMap {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get count of variables (all).
ASMJIT_INLINE uint32_t getVaCount() const {
return _vaCount;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Variables count.
uint32_t _vaCount;
};
// ============================================================================
// [asmjit::VarState]
// ============================================================================
//! Variables' state.
struct VarState {};
// ============================================================================
// [asmjit::TypeId / VarMapping]
// ============================================================================
//! Function builder 'void' type.
struct Void {};
//! Function builder 'int8_t' type.
struct Int8Type {};
//! Function builder 'uint8_t' type.
struct UInt8Type {};
//! Function builder 'int16_t' type.
struct Int16Type {};
//! Function builder 'uint16_t' type.
struct UInt16Type {};
//! Function builder 'int32_t' type.
struct Int32Type {};
//! Function builder 'uint32_t' type.
struct UInt32Type {};
//! Function builder 'int64_t' type.
struct Int64Type {};
//! Function builder 'uint64_t' type.
struct UInt64Type {};
//! Function builder 'intptr_t' type.
struct IntPtrType {};
//! Function builder 'uintptr_t' type.
struct UIntPtrType {};
//! Function builder 'float' type.
struct FloatType {};
//! Function builder 'double' type.
struct DoubleType {};
#if !defined(ASMJIT_DOCGEN)
template<typename T>
struct TypeId {
// Left empty to report any type, which is not known to asmjit.
};
template<typename T>
struct TypeId<T*> {
enum { kId = kVarTypeIntPtr };
};
#define ASMJIT_TYPE_ID(_T_, _Id_) \
template<> \
struct TypeId<_T_> { enum { kId = _Id_ }; }
ASMJIT_TYPE_ID(void , kInvalidVar);
ASMJIT_TYPE_ID(char , IntTraits<char>::kIsSigned ? kVarTypeInt8 : kVarTypeUInt8);
ASMJIT_TYPE_ID(signed char , kVarTypeInt8);
ASMJIT_TYPE_ID(unsigned char, kVarTypeUInt8);
ASMJIT_TYPE_ID(int16_t , kVarTypeInt16);
ASMJIT_TYPE_ID(uint16_t , kVarTypeUInt16);
ASMJIT_TYPE_ID(int32_t , kVarTypeInt32);
ASMJIT_TYPE_ID(uint32_t , kVarTypeUInt32);
ASMJIT_TYPE_ID(int64_t , kVarTypeInt64);
ASMJIT_TYPE_ID(uint64_t , kVarTypeUInt64);
ASMJIT_TYPE_ID(float , kVarTypeFp32);
ASMJIT_TYPE_ID(double , kVarTypeFp64);
ASMJIT_TYPE_ID(Void , kInvalidVar);
ASMJIT_TYPE_ID(Int8Type , kVarTypeInt8);
ASMJIT_TYPE_ID(UInt8Type , kVarTypeUInt8);
ASMJIT_TYPE_ID(Int16Type , kVarTypeInt16);
ASMJIT_TYPE_ID(UInt16Type , kVarTypeUInt16);
ASMJIT_TYPE_ID(Int32Type , kVarTypeUInt32);
ASMJIT_TYPE_ID(UInt32Type , kVarTypeUInt32);
ASMJIT_TYPE_ID(Int64Type , kVarTypeUInt64);
ASMJIT_TYPE_ID(UInt64Type , kVarTypeUInt64);
ASMJIT_TYPE_ID(FloatType , kVarTypeFp32);
ASMJIT_TYPE_ID(DoubleType , kVarTypeFp64);
#endif // !ASMJIT_DOCGEN
// ============================================================================
// [asmjit::FuncInOut]
// ============================================================================
//! Function in/out - argument or return value translated from `FuncPrototype`.
struct FuncInOut {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uint32_t getVarType() const { return _varType; }
ASMJIT_INLINE bool hasRegIndex() const { return _regIndex != kInvalidReg; }
ASMJIT_INLINE uint32_t getRegIndex() const { return _regIndex; }
ASMJIT_INLINE bool hasStackOffset() const { return _stackOffset != kFuncStackInvalid; }
ASMJIT_INLINE int32_t getStackOffset() const { return static_cast<int32_t>(_stackOffset); }
//! Get whether the argument / return value is assigned.
ASMJIT_INLINE bool isSet() const {
return (_regIndex != kInvalidReg) | (_stackOffset != kFuncStackInvalid);
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the function argument to "unassigned state".
ASMJIT_INLINE void reset() { _packed = 0xFFFFFFFF; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
union {
struct {
//! Variable type, see `VarType`.
uint8_t _varType;
//! Register index if argument / return value is a register.
uint8_t _regIndex;
//! Stack offset if argument / return value is on the stack.
int16_t _stackOffset;
};
//! All members packed into single 32-bit integer.
uint32_t _packed;
};
};
// ============================================================================
// [asmjit::FuncPrototype]
// ============================================================================
//! Function prototype.
//!
//! Function prototype contains information about function return type, count
//! of arguments and their types. Function prototype is a low level structure
//! which doesn't contain platform specific or calling convention specific
//! information. Function prototype is used to create a `FuncDecl`.
struct FuncPrototype {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get function return value.
ASMJIT_INLINE uint32_t getRet() const { return _ret; }
//! Get function arguments' IDs.
ASMJIT_INLINE const uint32_t* getArgList() const { return _argList; }
//! Get count of function arguments.
ASMJIT_INLINE uint32_t getArgCount() const { return _argCount; }
//! Get argument at index `id`.
ASMJIT_INLINE uint32_t getArg(uint32_t id) const {
ASMJIT_ASSERT(id < _argCount);
return _argList[id];
}
//! Set function definition - return type and arguments.
ASMJIT_INLINE void _setPrototype(uint32_t ret, const uint32_t* argList, uint32_t argCount) {
_ret = ret;
_argList = argList;
_argCount = argCount;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint32_t _ret;
uint32_t _argCount;
const uint32_t* _argList;
};
// ============================================================================
// [asmjit::FuncBuilderX]
// ============================================================================
//! Custom function builder for up to 32 function arguments.
struct FuncBuilderX : public FuncPrototype {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncBuilderX() {
_setPrototype(kInvalidVar, _builderArgList, 0);
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Set return type to `retType`.
ASMJIT_INLINE void setRet(uint32_t retType) {
_ret = retType;
}
ASMJIT_INLINE void setArg(uint32_t id, uint32_t type) {
ASMJIT_ASSERT(id < _argCount);
_builderArgList[id] = type;
}
ASMJIT_INLINE void addArg(uint32_t type) {
ASMJIT_ASSERT(_argCount < kFuncArgCount);
_builderArgList[_argCount++] = type;
}
template<typename T>
ASMJIT_INLINE void setRetT() {
setRet(TypeId<T>::kId);
}
template<typename T>
ASMJIT_INLINE void setArgT(uint32_t id) {
setArg(id, TypeId<T>::kId);
}
template<typename T>
ASMJIT_INLINE void addArgT() {
addArg(TypeId<T>::kId);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint32_t _builderArgList[kFuncArgCount];
};
//! \internal
#define T(_Type_) TypeId<_Type_>::kId
//! Function prototype (no args).
template<typename RET>
struct FuncBuilder0 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder0() {
_setPrototype(T(RET), NULL, 0);
}
};
//! Function prototype (1 argument).
template<typename RET, typename P0>
struct FuncBuilder1 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder1() {
static const uint32_t args[] = { T(P0) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (2 arguments).
template<typename RET, typename P0, typename P1>
struct FuncBuilder2 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder2() {
static const uint32_t args[] = { T(P0), T(P1) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (3 arguments).
template<typename RET, typename P0, typename P1, typename P2>
struct FuncBuilder3 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder3() {
static const uint32_t args[] = { T(P0), T(P1), T(P2) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (4 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3>
struct FuncBuilder4 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder4() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (5 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4>
struct FuncBuilder5 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder5() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (6 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
struct FuncBuilder6 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder6() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (7 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
struct FuncBuilder7 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder7() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (8 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
struct FuncBuilder8 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder8() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (9 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
struct FuncBuilder9 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder9() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7), T(P8) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Function prototype (10 arguments).
template<typename RET, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
struct FuncBuilder10 : public FuncPrototype {
ASMJIT_INLINE FuncBuilder10() {
static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7), T(P8), T(P9) };
_setPrototype(T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
#undef T
// ============================================================================
// [asmjit::FuncDecl]
// ============================================================================
//! Function declaration.
struct FuncDecl {
// --------------------------------------------------------------------------
// [Accessors - Calling Convention]
// --------------------------------------------------------------------------
//! Get function calling convention, see `FuncConv`.
ASMJIT_INLINE uint32_t getConvention() const { return _convention; }
//! Get whether the callee pops the stack.
ASMJIT_INLINE uint32_t getCalleePopsStack() const { return _calleePopsStack; }
//! Get direction of arguments passed on the stack.
//!
//! Direction should be always `kFuncDirRtl`.
//!
//! \note This is related to used calling convention, it's not affected by
//! number of function arguments or their types.
ASMJIT_INLINE uint32_t getDirection() const { return _direction; }
//! Get stack size needed for function arguments passed on the stack.
ASMJIT_INLINE uint32_t getArgStackSize() const { return _argStackSize; }
//! Get size of "Red Zone".
ASMJIT_INLINE uint32_t getRedZoneSize() const { return _redZoneSize; }
//! Get size of "Spill Zone".
ASMJIT_INLINE uint32_t getSpillZoneSize() const { return _spillZoneSize; }
// --------------------------------------------------------------------------
// [Accessors - Arguments and Return]
// --------------------------------------------------------------------------
//! Get whether the function has a return value.
ASMJIT_INLINE bool hasRet() const { return _retCount != 0; }
//! Get count of function return values.
ASMJIT_INLINE uint32_t getRetCount() const { return _retCount; }
//! Get function return value.
ASMJIT_INLINE FuncInOut& getRet(uint32_t index = kFuncRetLo) { return _retList[index]; }
//! Get function return value.
ASMJIT_INLINE const FuncInOut& getRet(uint32_t index = kFuncRetLo) const { return _retList[index]; }
//! Get count of function arguments.
ASMJIT_INLINE uint32_t getArgCount() const { return _argCount; }
//! Get function arguments array.
ASMJIT_INLINE FuncInOut* getArgList() { return _argList; }
//! Get function arguments array (const).
ASMJIT_INLINE const FuncInOut* getArgList() const { return _argList; }
//! Get function argument at index `index`.
ASMJIT_INLINE FuncInOut& getArg(size_t index) {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _argList[index];
}
//! Get function argument at index `index`.
ASMJIT_INLINE const FuncInOut& getArg(size_t index) const {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _argList[index];
}
ASMJIT_INLINE void resetArg(size_t index) {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
_argList[index].reset();
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Calling convention.
uint8_t _convention;
//! Whether a callee pops stack.
uint8_t _calleePopsStack : 1;
//! Direction for arguments passed on the stack, see `FuncDir`.
uint8_t _direction : 1;
//! Reserved #0 (alignment).
uint8_t _reserved0 : 6;
//! Count of arguments in `_argList`.
uint8_t _argCount;
//! Count of return value(s).
uint8_t _retCount;
//! Count of bytes consumed by arguments on the stack (aligned).
uint32_t _argStackSize;
//! Size of "Red Zone".
//!
//! \note Used by AMD64-ABI (128 bytes).
uint16_t _redZoneSize;
//! Size of "Spill Zone".
//!
//! \note Used by WIN64-ABI (32 bytes).
uint16_t _spillZoneSize;
//! Function arguments (including HI arguments) mapped to physical
//! registers and stack offset.
FuncInOut _argList[kFuncArgCountLoHi];
//! Function return value(s).
FuncInOut _retList[2];
};
// ============================================================================
// [asmjit::Node]
// ============================================================================
//! Base node.
//!
//! `Every` node represents an abstract instruction, directive, label, or
//! macro-instruction generated by compiler.
struct Node {
ASMJIT_NO_COPY(Node)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create new `Node`.
//!
//! \note Always use compiler to create nodes.
ASMJIT_INLINE Node(Compiler* compiler, uint32_t type); // Defined-Later.
//! Destroy `Node`.
ASMJIT_INLINE ~Node() {}
// --------------------------------------------------------------------------
// [Accessors - List]
// --------------------------------------------------------------------------
//! Get previous node in the compiler stream.
ASMJIT_INLINE Node* getPrev() const {
return _prev;
}
//! Get next node in the compiler stream.
ASMJIT_INLINE Node* getNext() const {
return _next;
}
// --------------------------------------------------------------------------
// [Accessors - Comment]
// --------------------------------------------------------------------------
//! Get comment string.
ASMJIT_INLINE const char* getComment() const {
return _comment;
}
//! Set comment string to `str`.
ASMJIT_INLINE void setComment(const char* comment) {
_comment = comment;
}
// --------------------------------------------------------------------------
// [Accessors - Type and Flags]
// --------------------------------------------------------------------------
//! Get node type, see `NodeType`.
ASMJIT_INLINE uint32_t getType() const {
return _type;
}
//! Get node flags.
ASMJIT_INLINE uint32_t getFlags() const {
return _flags;
}
//! Get whether the instruction has flag `flag`.
ASMJIT_INLINE bool hasFlag(uint32_t flag) const {
return (static_cast<uint32_t>(_flags) & flag) != 0;
}
//! Set node flags to `flags`.
ASMJIT_INLINE void setFlags(uint32_t flags) {
_flags = static_cast<uint16_t>(flags);
}
//! Add instruction `flags`.
ASMJIT_INLINE void orFlags(uint32_t flags) {
_flags |= static_cast<uint16_t>(flags);
}
//! And instruction `flags`.
ASMJIT_INLINE void andFlags(uint32_t flags) {
_flags &= static_cast<uint16_t>(flags);
}
//! Clear instruction `flags`.
ASMJIT_INLINE void andNotFlags(uint32_t flags) {
_flags &= ~static_cast<uint16_t>(flags);
}
//! Get whether the node has beed fetched.
ASMJIT_INLINE bool isFetched() const {
return _flowId != 0;
}
//! Get whether the node has been translated.
ASMJIT_INLINE bool isTranslated() const {
return hasFlag(kNodeFlagIsTranslated);
}
//! Get whether the node has been translated.
ASMJIT_INLINE bool isScheduled() const {
return hasFlag(kNodeFlagIsScheduled);
}
//! Get whether the node is informative only and can be safely removed after
//! translation.
//!
//! Informative nodes are comments and hints.
ASMJIT_INLINE bool isInformative() const {
return hasFlag(kNodeFlagIsInformative);
}
//! Whether the node is `InstNode` and unconditional jump.
ASMJIT_INLINE bool isJmp() const { return hasFlag(kNodeFlagIsJmp); }
//! Whether the node is `InstNode` and conditional jump.
ASMJIT_INLINE bool isJcc() const { return hasFlag(kNodeFlagIsJcc); }
//! Whether the node is `InstNode` and conditional/unconditional jump.
ASMJIT_INLINE bool isJmpOrJcc() const { return hasFlag(kNodeFlagIsJmp | kNodeFlagIsJcc); }
//! Whether the node is `InstNode` and return.
ASMJIT_INLINE bool isRet() const { return hasFlag(kNodeFlagIsRet); }
//! Get whether the node is `InstNode` and the instruction is special.
ASMJIT_INLINE bool isSpecial() const { return hasFlag(kNodeFlagIsSpecial); }
//! Get whether the node is `InstNode` and the instruction uses x87-FPU.
ASMJIT_INLINE bool isFp() const { return hasFlag(kNodeFlagIsFp); }
// --------------------------------------------------------------------------
// [Accessors - FlowId]
// --------------------------------------------------------------------------
//! Get flow index.
ASMJIT_INLINE uint32_t getFlowId() const { return _flowId; }
//! Set flow index.
ASMJIT_INLINE void setFlowId(uint32_t flowId) { _flowId = flowId; }
// --------------------------------------------------------------------------
// [Accessors - VarMap]
// --------------------------------------------------------------------------
//! Get whether node contains variable allocation instructions.
ASMJIT_INLINE bool hasMap() const {
return _map != NULL;
}
//! Get variable allocation instructions.
ASMJIT_INLINE VarMap* getMap() const {
return _map;
}
//! Get variable allocation instructions casted to `T*`.
template<typename T>
ASMJIT_INLINE T* getMap() const {
return static_cast<T*>(_map);
}
//! Set variable allocation instructions.
ASMJIT_INLINE void setMap(VarMap* map) {
_map = map;
}
// --------------------------------------------------------------------------
// [Accessors - VarState]
// --------------------------------------------------------------------------
//! Get node state.
ASMJIT_INLINE VarState* getState() const {
return _state;
}
//! Get node state casted to `T*`.
template<typename T>
ASMJIT_INLINE T* getState() const {
return static_cast<VarState*>(_state);
}
//! Set node state.
ASMJIT_INLINE void setState(VarState* state) {
_state = state;
}
// --------------------------------------------------------------------------
// [Accessors - Liveness]
// --------------------------------------------------------------------------
//! Get whether the node has variable liveness bits.
ASMJIT_INLINE bool hasLiveness() const {
return _liveness != NULL;
}
//! Get variable liveness bits.
ASMJIT_INLINE VarBits* getLiveness() const {
return _liveness;
}
//! Set variable liveness bits.
ASMJIT_INLINE void setLiveness(VarBits* liveness) {
_liveness = liveness;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Previous node.
Node* _prev;
//! Next node.
Node* _next;
//! Node type, see `NodeType`.
uint8_t _type;
//! Operands count (if the node has operands, otherwise zero).
uint8_t _opCount;
//! Node flags, different meaning for every node type.
uint16_t _flags;
//! Flow index.
uint32_t _flowId;
//! Inline comment string, initially set to NULL.
const char* _comment;
//! Variable mapping (VarAttr to VarData), initially NULL, filled during
//! fetch phase.
VarMap* _map;
//! Variable liveness bits (initially NULL, filled by analysis phase).
VarBits* _liveness;
//! Saved state.
//!
//! Initially NULL, not all nodes have saved state, only branch/flow control
//! nodes.
VarState* _state;
};
// ============================================================================
// [asmjit::AlignNode]
// ============================================================================
//! Align node.
struct AlignNode : public Node {
ASMJIT_NO_COPY(AlignNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `AlignNode` instance.
ASMJIT_INLINE AlignNode(Compiler* compiler, uint32_t mode, uint32_t offset) :
Node(compiler, kNodeTypeAlign) {
_mode = mode;
_offset = offset;
}
//! Destroy the `AlignNode` instance.
ASMJIT_INLINE ~AlignNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get alignment mode.
ASMJIT_INLINE uint32_t getMode() const {
return _mode;
}
//! Set alignment mode.
ASMJIT_INLINE void setMode(uint32_t mode) {
_mode = mode;
}
//! Get align offset in bytes.
ASMJIT_INLINE uint32_t getOffset() const {
return _offset;
}
//! Set align offset in bytes to `offset`.
ASMJIT_INLINE void setOffset(uint32_t offset) {
_offset = offset;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Alignment mode, see \ref AlignMode.
uint32_t _mode;
//! Alignment offset in bytes.
uint32_t _offset;
};
// ============================================================================
// [asmjit::EmbedNode]
// ============================================================================
//! Embed node.
//!
//! Embed node is used to embed data into final assembler stream. The data is
//! considered to be RAW; No analysis is performed on RAW data.
struct EmbedNode : public Node {
ASMJIT_NO_COPY(EmbedNode)
// --------------------------------------------------------------------------
// [Enums]
// --------------------------------------------------------------------------
enum { kInlineBufferSize = 8 };
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `EmbedNode` instance.
ASMJIT_INLINE EmbedNode(Compiler* compiler, void* data, uint32_t size) :
Node(compiler, kNodeTypeEmbed) {
_size = size;
if (size <= kInlineBufferSize) {
if (data != NULL)
::memcpy(_data.buf, data, size);
}
else {
_data.ptr = static_cast<uint8_t*>(data);
}
}
//! Destroy the `EmbedNode` instance.
ASMJIT_INLINE ~EmbedNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get pointer to data.
uint8_t* getData() { return getSize() <= kInlineBufferSize ? const_cast<uint8_t*>(_data.buf) : _data.ptr; }
//! Get size of data.
uint32_t getSize() const { return _size; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Size of the embedded data.
uint32_t _size;
union {
//! data buffer.
uint8_t buf[kInlineBufferSize];
//! Data buffer.
uint8_t* ptr;
} _data;
};
// ============================================================================
// [asmjit::CommentNode]
// ============================================================================
//! Comment node.
//!
//! Comments allows to comment your assembler stream for better debugging
//! and visualization. Comments are usually ignored in release builds unless
//! the logger is present.
struct CommentNode : public Node {
ASMJIT_NO_COPY(CommentNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `CommentNode` instance.
ASMJIT_INLINE CommentNode(Compiler* compiler, const char* comment) : Node(compiler, kNodeTypeComment) {
orFlags(kNodeFlagIsInformative);
_comment = comment;
}
//! Destroy the `CommentNode` instance.
ASMJIT_INLINE ~CommentNode() {}
};
// ============================================================================
// [asmjit::HintNode]
// ============================================================================
//! Hint node.
struct HintNode : public Node {
ASMJIT_NO_COPY(HintNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `HintNode` instance.
ASMJIT_INLINE HintNode(Compiler* compiler, VarData* vd, uint32_t hint, uint32_t value) :
Node(compiler, kNodeTypeHint) {
orFlags(kNodeFlagIsInformative);
_vd = vd;
_hint = hint;
_value = value;
}
//! Destroy the `HintNode` instance.
ASMJIT_INLINE ~HintNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get variable.
ASMJIT_INLINE VarData* getVd() const { return _vd; }
//! Get hint it (see `kVarHint)`.
ASMJIT_INLINE uint32_t getHint() const{ return _hint; }
//! Set hint it (see `kVarHint)`.
ASMJIT_INLINE void setHint(uint32_t hint) { _hint = hint; }
//! Get hint value.
ASMJIT_INLINE uint32_t getValue() const { return _value; }
//! Set hint value.
ASMJIT_INLINE void setValue(uint32_t value) { _value = value; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Variable.
VarData* _vd;
//! Hint id.
uint32_t _hint;
//! Value.
uint32_t _value;
};
// ============================================================================
// [asmjit::TargetNode]
// ============================================================================
//! label node.
struct TargetNode : public Node {
ASMJIT_NO_COPY(TargetNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `TargetNode` instance.
ASMJIT_INLINE TargetNode(Compiler* compiler, uint32_t labelId) : Node(compiler, kNodeTypeTarget) {
_id = labelId;
_numRefs = 0;
_offset = -1;
_from = NULL;
}
//! Destroy the `TargetNode` instance.
ASMJIT_INLINE ~TargetNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get target label.
ASMJIT_INLINE Label getLabel() const { return Label(_id); }
//! Get target label id.
ASMJIT_INLINE uint32_t getLabelId() const { return _id; }
//! Get first jmp instruction.
ASMJIT_INLINE JumpNode* getFrom() const { return _from; }
//! Get whether the node has assigned state.
ASMJIT_INLINE bool hasState() const { return _state != NULL; }
//! Get state for this target.
ASMJIT_INLINE VarState* getState() const { return _state; }
//! Set state for this target.
ASMJIT_INLINE void setState(VarState* state) { _state = state; }
//! Get number of jumps to this target.
ASMJIT_INLINE uint32_t getNumRefs() const { return _numRefs; }
//! Set number of jumps to this target.
ASMJIT_INLINE void setNumRefs(uint32_t i) { _numRefs = i; }
//! Add number of jumps to this target.
ASMJIT_INLINE void addNumRefs(uint32_t i = 1) { _numRefs += i; }
//! Subtract number of jumps to this target.
ASMJIT_INLINE void subNumRefs(uint32_t i = 1) { _numRefs -= i; }
//! Get the label offset.
//!
//! \note Only valid after the content has been serialized to the `Assembler`.
ASMJIT_INLINE intptr_t getOffset() const { return _offset; }
//! Set the label offset.
ASMJIT_INLINE void setOffset(intptr_t offset) { _offset = offset; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Label id.
uint32_t _id;
//! Count of jumps here.
uint32_t _numRefs;
//! Label offset, after serialization.
intptr_t _offset;
//! First jump instruction that points to this target (label).
JumpNode* _from;
};
// ============================================================================
// [asmjit::InstNode]
// ============================================================================
//! Instruction node.
struct InstNode : public Node {
ASMJIT_NO_COPY(InstNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `InstNode` instance.
ASMJIT_INLINE InstNode(Compiler* compiler, uint32_t instId, uint32_t instOptions, Operand* opList, uint32_t opCount) :
Node(compiler, kNodeTypeInst) {
_instId = static_cast<uint16_t>(instId);
_reserved = 0;
_instOptions = instOptions;
_opCount = static_cast<uint8_t>(opCount);
_opList = opList;
_updateMemOp();
}
//! Destroy the `InstNode` instance.
ASMJIT_INLINE ~InstNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get instruction ID, see `X86InstId`.
ASMJIT_INLINE uint32_t getInstId() const {
return _instId;
}
//! Set instruction ID to `instId`.
//!
//! Please do not modify instruction code if you don't know what are you
//! doing. Incorrect instruction code or operands can cause assertion failure.
ASMJIT_INLINE void setInstId(uint32_t instId) {
_instId = static_cast<uint16_t>(instId);
}
//! Whether the instruction is an unconditional jump or whether the
//! instruction is a conditional jump which is likely to be taken.
ASMJIT_INLINE bool isTaken() const {
return hasFlag(kNodeFlagIsTaken);
}
//! Get emit options.
ASMJIT_INLINE uint32_t getOptions() const {
return _instOptions;
}
//! Set emit options.
ASMJIT_INLINE void setOptions(uint32_t options) {
_instOptions = options;
}
//! Add emit options.
ASMJIT_INLINE void addOptions(uint32_t options) {
_instOptions |= options;
}
//! Mask emit options.
ASMJIT_INLINE void andOptions(uint32_t options) {
_instOptions &= options;
}
//! Clear emit options.
ASMJIT_INLINE void delOptions(uint32_t options) {
_instOptions &= ~options;
}
//! Get operands list.
ASMJIT_INLINE Operand* getOpList() {
return _opList;
}
//! \overload
ASMJIT_INLINE const Operand* getOpList() const {
return _opList;
}
//! Get operands count.
ASMJIT_INLINE uint32_t getOpCount() const {
return _opCount;
}
//! Get whether the instruction contains a memory operand.
ASMJIT_INLINE bool hasMemOp() const {
return _memOpIndex != 0xFF;
}
//! Set memory operand index (in opList), 0xFF means that instruction
//! doesn't have a memory operand.
ASMJIT_INLINE void setMemOpIndex(uint32_t index) {
_memOpIndex = static_cast<uint8_t>(index);
}
//! Reset memory operand index, setting it to 0xFF.
ASMJIT_INLINE void resetMemOpIndex() {
_memOpIndex = 0xFF;
}
//! Get memory operand.
//!
//! Can only be called if the instruction has such operand, see `hasMemOp()`.
ASMJIT_INLINE BaseMem* getMemOp() const {
ASMJIT_ASSERT(hasMemOp());
return static_cast<BaseMem*>(&_opList[_memOpIndex]);
}
//! \overload
template<typename T>
ASMJIT_INLINE T* getMemOp() const {
ASMJIT_ASSERT(hasMemOp());
return static_cast<T*>(&_opList[_memOpIndex]);
}
// --------------------------------------------------------------------------
// [Utils]
// --------------------------------------------------------------------------
ASMJIT_INLINE void _updateMemOp() {
Operand* opList = getOpList();
uint32_t opCount = getOpCount();
uint32_t i;
for (i = 0; i < opCount; i++)
if (opList[i].isMem())
goto _Update;
i = 0xFF;
_Update:
setMemOpIndex(i);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Instruction ID, see `InstId`.
uint16_t _instId;
//! \internal
uint8_t _memOpIndex;
//! \internal
uint8_t _reserved;
//! Instruction options, see `InstOptions`.
uint32_t _instOptions;
//! Operands list.
Operand* _opList;
};
// ============================================================================
// [asmjit::JumpNode]
// ============================================================================
//! Jump node.
struct JumpNode : public InstNode {
ASMJIT_NO_COPY(JumpNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE JumpNode(Compiler* compiler, uint32_t code, uint32_t options, Operand* opList, uint32_t opCount) :
InstNode(compiler, code, options, opList, opCount) {}
ASMJIT_INLINE ~JumpNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE TargetNode* getTarget() const { return _target; }
ASMJIT_INLINE JumpNode* getJumpNext() const { return _jumpNext; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Target node.
TargetNode* _target;
//! Next jump to the same target in a single linked-list.
JumpNode *_jumpNext;
};
// ============================================================================
// [asmjit::FuncNode]
// ============================================================================
//! Function declaration node.
//!
//! Functions are base blocks for generating assembler output. Each generated
//! assembler stream needs standard entry and leave sequences which are compatible
//! with the operating system ABI.
//!
//! `FuncNode` can be used to generate function prolog and epilog which are
//! compatible with a given function calling convention and to allocate and
//! manage variables that can be allocated/spilled during compilation phase.
struct FuncNode : public Node {
ASMJIT_NO_COPY(FuncNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `FuncNode` instance.
//!
//! Always use `Compiler::addFunc()` to create a `FuncNode` instance.
ASMJIT_INLINE FuncNode(Compiler* compiler) :
Node(compiler, kNodeTypeFunc),
_entryNode(NULL),
_exitNode(NULL),
_decl(NULL),
_end(NULL),
_argList(NULL),
_funcHints(IntUtil::mask(kFuncHintNaked)),
_funcFlags(0),
_expectedStackAlignment(0),
_requiredStackAlignment(0),
_redZoneSize(0),
_spillZoneSize(0),
_argStackSize(0),
_memStackSize(0),
_callStackSize(0) {}
//! Destroy the `FuncNode` instance.
ASMJIT_INLINE ~FuncNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get function entry `TargetNode`.
ASMJIT_INLINE TargetNode* getEntryNode() const { return _entryNode; }
//! Get function exit `TargetNode`.
ASMJIT_INLINE TargetNode* getExitNode() const { return _exitNode; }
//! Get function entry label.
ASMJIT_INLINE Label getEntryLabel() const { return _entryNode->getLabel(); }
//! Get function exit label.
ASMJIT_INLINE Label getExitLabel() const { return _exitNode->getLabel(); }
//! Get function `EndNode`.
ASMJIT_INLINE EndNode* getEnd() const { return _end; }
//! Get function declaration.
ASMJIT_INLINE FuncDecl* getDecl() const { return _decl; }
//! Get arguments list.
ASMJIT_INLINE VarData** getArgList() const { return _argList; }
//! Get arguments count.
ASMJIT_INLINE uint32_t getArgCount() const { return _decl->getArgCount(); }
//! Get argument at `i`.
ASMJIT_INLINE VarData* getArg(uint32_t i) const {
ASMJIT_ASSERT(i < getArgCount());
return _argList[i];
}
//! Set argument at `i`.
ASMJIT_INLINE void setArg(uint32_t i, VarData* vd) {
ASMJIT_ASSERT(i < getArgCount());
_argList[i] = vd;
}
//! Reset argument at `i`.
ASMJIT_INLINE void resetArg(uint32_t i) {
ASMJIT_ASSERT(i < getArgCount());
_argList[i] = NULL;
}
//! Get function hints.
ASMJIT_INLINE uint32_t getFuncHints() const { return _funcHints; }
//! Get function flags.
ASMJIT_INLINE uint32_t getFuncFlags() const { return _funcFlags; }
//! Get whether the _funcFlags has `flag`
ASMJIT_INLINE bool hasFuncFlag(uint32_t flag) const { return (_funcFlags & flag) != 0; }
//! Set function `flag`.
ASMJIT_INLINE void addFuncFlags(uint32_t flags) { _funcFlags |= flags; }
//! Clear function `flag`.
ASMJIT_INLINE void clearFuncFlags(uint32_t flags) { _funcFlags &= ~flags; }
//! Get whether the function is naked.
ASMJIT_INLINE bool isNaked() const { return hasFuncFlag(kFuncFlagIsNaked); }
//! Get whether the function is also a caller.
ASMJIT_INLINE bool isCaller() const { return hasFuncFlag(kFuncFlagIsCaller); }
//! Get whether the required stack alignment is lower than expected one,
//! thus it has to be aligned manually.
ASMJIT_INLINE bool isStackMisaligned() const { return hasFuncFlag(kFuncFlagIsStackMisaligned); }
//! Get whether the stack pointer is adjusted inside function prolog/epilog.
ASMJIT_INLINE bool isStackAdjusted() const { return hasFuncFlag(kFuncFlagIsStackAdjusted); }
//! Get whether the function is finished.
ASMJIT_INLINE bool isFinished() const { return hasFuncFlag(kFuncFlagIsFinished); }
//! Get expected stack alignment.
ASMJIT_INLINE uint32_t getExpectedStackAlignment() const { return _expectedStackAlignment; }
//! Set expected stack alignment.
ASMJIT_INLINE void setExpectedStackAlignment(uint32_t alignment) { _expectedStackAlignment = alignment; }
//! Get required stack alignment.
ASMJIT_INLINE uint32_t getRequiredStackAlignment() const { return _requiredStackAlignment; }
//! Set required stack alignment.
ASMJIT_INLINE void setRequiredStackAlignment(uint32_t alignment) { _requiredStackAlignment = alignment; }
//! Update required stack alignment so it's not lower than expected
//! stack alignment.
ASMJIT_INLINE void updateRequiredStackAlignment() {
if (_requiredStackAlignment <= _expectedStackAlignment) {
_requiredStackAlignment = _expectedStackAlignment;
clearFuncFlags(kFuncFlagIsStackMisaligned);
}
else {
addFuncFlags(kFuncFlagIsStackMisaligned);
}
}
//! Set stack "Red Zone" size.
ASMJIT_INLINE uint32_t getRedZoneSize() const { return _redZoneSize; }
//! Get stack "Red Zone" size.
ASMJIT_INLINE void setRedZoneSize(uint32_t s) { _redZoneSize = static_cast<uint16_t>(s); }
//! Set stack "Spill Zone" size.
ASMJIT_INLINE uint32_t getSpillZoneSize() const { return _spillZoneSize; }
//! Get stack "Spill Zone" size.
ASMJIT_INLINE void setSpillZoneSize(uint32_t s) { _spillZoneSize = static_cast<uint16_t>(s); }
//! Get stack size used by function arguments.
ASMJIT_INLINE uint32_t getArgStackSize() const { return _argStackSize; }
//! Get stack size used by variables and memory allocated on the stack.
ASMJIT_INLINE uint32_t getMemStackSize() const { return _memStackSize; }
//! Get stack size used by function calls.
ASMJIT_INLINE uint32_t getCallStackSize() const { return _callStackSize; }
//! Merge stack size used by function call with `s`.
ASMJIT_INLINE void mergeCallStackSize(uint32_t s) { if (_callStackSize < s) _callStackSize = s; }
// --------------------------------------------------------------------------
// [Hints]
// --------------------------------------------------------------------------
//! Set function hint.
ASMJIT_INLINE void setHint(uint32_t hint, uint32_t value) {
ASMJIT_ASSERT(hint <= 31);
ASMJIT_ASSERT(value <= 1);
_funcHints &= ~(1 << hint);
_funcHints |= (value << hint);
}
//! Get function hint.
ASMJIT_INLINE uint32_t getHint(uint32_t hint) const {
ASMJIT_ASSERT(hint <= 31);
return (_funcHints >> hint) & 0x1;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Function entry.
TargetNode* _entryNode;
//! Function exit.
TargetNode* _exitNode;
//! Function declaration.
FuncDecl* _decl;
//! Function end.
EndNode* _end;
//! Arguments list as `VarData`.
VarData** _argList;
//! Function hints;
uint32_t _funcHints;
//! Function flags.
uint32_t _funcFlags;
//! Expected stack alignment (we depend on this value).
//!
//! \note It can be global alignment given by the OS or described by an
//! target platform ABI.
uint32_t _expectedStackAlignment;
//! Required stack alignment (usually for multimedia instructions).
uint32_t _requiredStackAlignment;
//! The "Red Zone" size - count of bytes which might be accessed without
//! adjusting the stack pointer.
uint16_t _redZoneSize;
//! Spill zone size (used by WIN64 ABI).
uint16_t _spillZoneSize;
//! Stack size needed for function arguments.
uint32_t _argStackSize;
//! Stack size needed for all variables and memory allocated on the stack.
uint32_t _memStackSize;
//! Stack size needed to call other functions.
uint32_t _callStackSize;
};
// ============================================================================
// [asmjit::EndNode]
// ============================================================================
//! End of function/block node.
struct EndNode : public Node {
ASMJIT_NO_COPY(EndNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `EndNode` instance.
ASMJIT_INLINE EndNode(Compiler* compiler) : Node(compiler, kNodeTypeEnd) {
_flags |= kNodeFlagIsRet;
}
//! Destroy the `EndNode` instance.
ASMJIT_INLINE ~EndNode() {}
};
// ============================================================================
// [asmjit::RetNode]
// ============================================================================
//! Function return node.
struct RetNode : public Node {
ASMJIT_NO_COPY(RetNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `RetNode` instance.
ASMJIT_INLINE RetNode(Compiler* compiler, const Operand& o0, const Operand& o1) : Node(compiler, kNodeTypeRet) {
_flags |= kNodeFlagIsRet;
_ret[0] = o0;
_ret[1] = o1;
}
//! Destroy the `RetNode` instance.
ASMJIT_INLINE ~RetNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the first return operand.
ASMJIT_INLINE Operand& getFirst() { return _ret[0]; }
//! \overload
ASMJIT_INLINE const Operand& getFirst() const { return _ret[0]; }
//! Get the second return operand.
ASMJIT_INLINE Operand& getSecond() { return _ret[1]; }
//! \overload
ASMJIT_INLINE const Operand& getSecond() const { return _ret[1]; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Ret operand(s).
Operand _ret[2];
};
// ============================================================================
// [asmjit::CallNode]
// ============================================================================
//! Function-call node.
struct CallNode : public Node {
ASMJIT_NO_COPY(CallNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `CallNode` instance.
ASMJIT_INLINE CallNode(Compiler* compiler, const Operand& target) :
Node(compiler, kNodeTypeCall),
_decl(NULL),
_target(target),
_args(NULL) {}
//! Destroy the `CallNode` instance.
ASMJIT_INLINE ~CallNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get function declaration.
ASMJIT_INLINE FuncDecl* getDecl() const { return _decl; }
//! Get target operand.
ASMJIT_INLINE Operand& getTarget() { return _target; }
//! \overload
ASMJIT_INLINE const Operand& getTarget() const { return _target; }
//! Get return at `i`.
ASMJIT_INLINE Operand& getRet(uint32_t i = 0) {
ASMJIT_ASSERT(i < 2);
return _ret[i];
}
//! \overload
ASMJIT_INLINE const Operand& getRet(uint32_t i = 0) const {
ASMJIT_ASSERT(i < 2);
return _ret[i];
}
//! Get argument at `i`.
ASMJIT_INLINE Operand& getArg(uint32_t i) {
ASMJIT_ASSERT(i < kFuncArgCountLoHi);
return _args[i];
}
//! \overload
ASMJIT_INLINE const Operand& getArg(uint32_t i) const {
ASMJIT_ASSERT(i < kFuncArgCountLoHi);
return _args[i];
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Function declaration.
FuncDecl* _decl;
//! Target (address of function, register, label, ...).
Operand _target;
//! Return.
Operand _ret[2];
//! Arguments.
Operand* _args;
};
// ============================================================================
// [asmjit::SArgNode]
// ============================================================================
//! Function-call 'argument on the stack' node.
struct SArgNode : public Node {
ASMJIT_NO_COPY(SArgNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `SArgNode` instance.
ASMJIT_INLINE SArgNode(Compiler* compiler, CallNode* call, VarData* sVd, VarData* cVd) :
Node(compiler, kNodeTypeSArg),
_call(call),
_sVd(sVd),
_cVd(cVd),
_args(0) {}
//! Destroy the `SArgNode` instance.
ASMJIT_INLINE ~SArgNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the associated function-call.
ASMJIT_INLINE CallNode* getCall() const { return _call; }
//! Get source variable.
ASMJIT_INLINE VarData* getSVd() const { return _sVd; }
//! Get conversion variable.
ASMJIT_INLINE VarData* getCVd() const { return _cVd; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Associated `CallNode`.
CallNode* _call;
//! Source variable.
VarData* _sVd;
//! Temporary variable used for conversion (or NULL).
VarData* _cVd;
//! Affected arguments bit-array.
uint32_t _args;
};
//! \}
// ============================================================================
// [asmjit::Compiler]
// ============================================================================
//! \addtogroup asmjit_base_general
//! \{
//! Base compiler.
//!
//! \sa Assembler.
struct ASMJIT_VCLASS Compiler : public CodeGen {
ASMJIT_NO_COPY(Compiler)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `Compiler` instance.
ASMJIT_API Compiler(Runtime* runtime);
//! Destroy the `Compiler` instance.
ASMJIT_API virtual ~Compiler();
// --------------------------------------------------------------------------
// [LookAhead]
// --------------------------------------------------------------------------
//! Get maximum look ahead.
ASMJIT_INLINE uint32_t getMaxLookAhead() const {
return _maxLookAhead;
}
//! Set maximum look ahead to `val`.
ASMJIT_INLINE void setMaxLookAhead(uint32_t val) {
_maxLookAhead = val;
}
// --------------------------------------------------------------------------
// [Clear / Reset]
// --------------------------------------------------------------------------
//! Reset the compiler.
//!
//! If `releaseMemory` is true all buffers will be released to the system.
ASMJIT_API void reset(bool releaseMemory = false);
// --------------------------------------------------------------------------
// [Nodes]
// --------------------------------------------------------------------------
template<typename T>
ASMJIT_INLINE T* newNode() {
void* p = _baseZone.alloc(sizeof(T));
return new(p) T(this);
}
template<typename T, typename P0>
ASMJIT_INLINE T* newNode(P0 p0) {
void* p = _baseZone.alloc(sizeof(T));
return new(p) T(this, p0);
}
template<typename T, typename P0, typename P1>
ASMJIT_INLINE T* newNode(P0 p0, P1 p1) {
void* p = _baseZone.alloc(sizeof(T));
return new(p) T(this, p0, p1);
}
template<typename T, typename P0, typename P1, typename P2>
ASMJIT_INLINE T* newNode(P0 p0, P1 p1, P2 p2) {
void* p = _baseZone.alloc(sizeof(T));
return new(p) T(this, p0, p1, p2);
}
//! Get first node.
ASMJIT_INLINE Node* getFirstNode() const { return _firstNode; }
//! Get last node.
ASMJIT_INLINE Node* getLastNode() const { return _lastNode; }
//! Get current node.
//!
//! \note If this method returns `NULL` it means that nothing has been emitted
//! yet.
ASMJIT_INLINE Node* getCursor() const { return _cursor; }
//! Set the current node without returning the previous node (private).
ASMJIT_INLINE void _setCursor(Node* node) { _cursor = node; }
//! Set the current node to `node` and return the previous one.
ASMJIT_API Node* setCursor(Node* node);
//! Add node `node` after current and set current to `node`.
ASMJIT_API Node* addNode(Node* node);
//! Add node before `ref`.
ASMJIT_API Node* addNodeBefore(Node* node, Node* ref);
//! Add node after `ref`.
ASMJIT_API Node* addNodeAfter(Node* node, Node* ref);
//! Remove node `node`.
ASMJIT_API Node* removeNode(Node* node);
//! Remove multiple nodes.
ASMJIT_API void removeNodes(Node* first, Node* last);
// --------------------------------------------------------------------------
// [Func]
// --------------------------------------------------------------------------
//! Get current function.
ASMJIT_INLINE FuncNode* getFunc() const { return _func; }
// --------------------------------------------------------------------------
// [Align]
// --------------------------------------------------------------------------
//! Create a new `AlignNode`.
ASMJIT_API AlignNode* newAlign(uint32_t mode, uint32_t offset);
//! Add a new `AlignNode`.
ASMJIT_API AlignNode* addAlign(uint32_t mode, uint32_t offset);
//! Align target buffer to `m` bytes.
//!
//! Typical usage of this is to align labels at start of the inner loops.
//!
//! Inserts `nop()` instructions or CPU optimized NOPs.
ASMJIT_INLINE AlignNode* align(uint32_t mode, uint32_t offset) {
return addAlign(mode, offset);
}
// --------------------------------------------------------------------------
// [Target]
// --------------------------------------------------------------------------
//! Create a new `TargetNode`.
ASMJIT_API TargetNode* newTarget();
//! Add a new `TargetNode`.
ASMJIT_API TargetNode* addTarget();
//! Get `TargetNode` by `id`.
ASMJIT_INLINE TargetNode* getTargetById(uint32_t id) {
ASMJIT_ASSERT(OperandUtil::isLabelId(id));
ASMJIT_ASSERT(id < _targetList.getLength());
return _targetList[id];
}
//! Get `TargetNode` by `label`.
ASMJIT_INLINE TargetNode* getTarget(const Label& label) {
return getTargetById(label.getId());
}
// --------------------------------------------------------------------------
// [Label]
// --------------------------------------------------------------------------
//! Get count of created labels.
ASMJIT_INLINE size_t getLabelsCount() const {
return _targetList.getLength();
}
//! Get whether `label` is created.
ASMJIT_INLINE bool isLabelValid(const Label& label) const {
return isLabelValid(label.getId());
}
//! \overload
ASMJIT_INLINE bool isLabelValid(uint32_t id) const {
return static_cast<size_t>(id) < _targetList.getLength();
}
//! Get `TargetNode` by `label`.
ASMJIT_INLINE TargetNode* getTargetByLabel(const Label& label) {
return getTargetByLabel(label.getId());
}
//! \overload
ASMJIT_INLINE TargetNode* getTargetByLabel(uint32_t id) {
ASMJIT_ASSERT(isLabelValid(id));
return _targetList[id];
}
//! Get `label` offset or -1 if the label is not bound.
//!
//! This method can be only called after the code has been serialized to the
//! `Assembler`, otherwise the offset returned will be -1 (even if the label
//! has been bound).
ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const {
return getLabelOffset(label.getId());
}
//! \overload
ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const {
ASMJIT_ASSERT(isLabelValid(id));
return _targetList[id]->getOffset();
}
//! \internal
//!
//! Create and initialize a new `Label`.
ASMJIT_API Error _newLabel(Label* dst);
//! Create and return a new `Label`.
ASMJIT_INLINE Label newLabel() {
Label result(NoInit);
_newLabel(&result);
return result;
}
//! Bind label to the current offset.
//!
//! \note Label can be bound only once!
ASMJIT_API Error bind(const Label& label);
// --------------------------------------------------------------------------
// [Embed]
// --------------------------------------------------------------------------
//! Create a new `EmbedNode`.
ASMJIT_API EmbedNode* newEmbed(const void* data, uint32_t size);
//! Add a new `EmbedNode`.
ASMJIT_API EmbedNode* addEmbed(const void* data, uint32_t size);
//! Embed data.
ASMJIT_INLINE EmbedNode* embed(const void* data, uint32_t size) {
return addEmbed(data, size);
}
// --------------------------------------------------------------------------
// [Comment]
// --------------------------------------------------------------------------
//! Create a new `CommentNode`.
ASMJIT_API CommentNode* newComment(const char* str);
//! Add a new `CommentNode`.
ASMJIT_API CommentNode* addComment(const char* str);
//! Emit a single comment line.
ASMJIT_API CommentNode* comment(const char* fmt, ...);
// --------------------------------------------------------------------------
// [Hint]
// --------------------------------------------------------------------------
//! Create a new `HintNode`.
ASMJIT_API HintNode* newHint(Var& var, uint32_t hint, uint32_t value);
//! Add a new `HintNode`.
ASMJIT_API HintNode* addHint(Var& var, uint32_t hint, uint32_t value);
// --------------------------------------------------------------------------
// [Vars]
// --------------------------------------------------------------------------
//! Get whether variable `var` is created.
ASMJIT_INLINE bool isVarValid(const Var& var) const {
return static_cast<size_t>(var.getId() & kOperandIdNum) < _varList.getLength();
}
//! \internal
//!
//! Get `VarData` by `var`.
ASMJIT_INLINE VarData* getVd(const Var& var) const {
return getVdById(var.getId());
}
//! \internal
//!
//! Get `VarData` by `id`.
ASMJIT_INLINE VarData* getVdById(uint32_t id) const {
ASMJIT_ASSERT(id != kInvalidValue);
ASMJIT_ASSERT(static_cast<size_t>(id & kOperandIdNum) < _varList.getLength());
return _varList[id & kOperandIdNum];
}
//! \internal
//!
//! Get an array of 'VarData*'.
ASMJIT_INLINE VarData** _getVdArray() const {
return const_cast<VarData**>(_varList.getData());
}
//! \internal
//!
//! Create a new `VarData`.
ASMJIT_API VarData* _newVd(uint32_t type, uint32_t size, uint32_t c, const char* name);
//! Create a new `Var`.
virtual Error _newVar(Var* var, uint32_t type, const char* name) = 0;
//! Alloc variable `var`.
ASMJIT_API void alloc(Var& var);
//! Alloc variable `var` using `regIndex` as a register index.
ASMJIT_API void alloc(Var& var, uint32_t regIndex);
//! Alloc variable `var` using `reg` as a register operand.
ASMJIT_API void alloc(Var& var, const Reg& reg);
//! Spill variable `var`.
ASMJIT_API void spill(Var& var);
//! Save variable `var` if the status is `modified` at this point.
ASMJIT_API void save(Var& var);
//! Unuse variable `var`.
ASMJIT_API void unuse(Var& var);
//! Alloc variable `var` (if initialized), but only if it's initialized.
ASMJIT_INLINE void allocUnsafe(Var& var) {
if (var.isInitialized())
alloc(var);
}
//! Alloc variable `var` (if initialized) using `regIndex` as a register index
ASMJIT_INLINE void allocUnsafe(Var& var, uint32_t regIndex) {
if (var.isInitialized())
alloc(var, regIndex);
}
//! Alloc variable `var` (if initialized) using `reg` as a register operand.
ASMJIT_INLINE void allocUnsafe(Var& var, const Reg& reg) {
if (var.isInitialized())
alloc(var, reg);
}
//! Spill variable `var` (if initialized).
ASMJIT_INLINE void spillUnsafe(Var& var) {
if (var.isInitialized())
spill(var);
}
//! Save variable `var` (if initialized) if the status is `modified` at this point.
ASMJIT_INLINE void saveUnsafe(Var& var) {
if (var.isInitialized())
save(var);
}
//! Unuse variable `var` (if initialized).
ASMJIT_INLINE void unuseUnsafe(Var& var) {
if (var.isInitialized())
unuse(var);
}
//! Get priority of variable `var`.
ASMJIT_API uint32_t getPriority(Var& var) const;
//! Set priority of variable `var` to `priority`.
ASMJIT_API void setPriority(Var& var, uint32_t priority);
//! Get save-on-unuse `var` property.
ASMJIT_API bool getSaveOnUnuse(Var& var) const;
//! Set save-on-unuse `var` property to `value`.
ASMJIT_API void setSaveOnUnuse(Var& var, bool value);
//! Rename variable `var` to `name`.
//!
//! \note Only new name will appear in the logger.
ASMJIT_API void rename(Var& var, const char* name);
// --------------------------------------------------------------------------
// [Stack]
// --------------------------------------------------------------------------
//! \internal
//!
//! Create a new memory chunk allocated on the current function's stack.
virtual Error _newStack(BaseMem* mem, uint32_t size, uint32_t alignment, const char* name) = 0;
// --------------------------------------------------------------------------
// [Const]
// --------------------------------------------------------------------------
//! \internal
//!
//! Put data to a constant-pool and get a memory reference to it.
virtual Error _newConst(BaseMem* mem, uint32_t scope, const void* data, size_t size) = 0;
// --------------------------------------------------------------------------
// [Assembler]
// --------------------------------------------------------------------------
//! Get an assembler instance that is associated with the compiler.
//!
//! \note One instance of `Assembler` is shared and has lifetime same as the
//! compiler, however, each call to `getAssembler()` resets the assembler so
//! new code can be serialized into it.
ASMJIT_API Assembler* getAssembler();
//! \internal
//!
//! Create a new `Assembler` instance associated with the compiler.
virtual Assembler* _newAssembler() = 0;
// --------------------------------------------------------------------------
// [Serialize]
// --------------------------------------------------------------------------
//! Serialize a compiled code to `assembler`.
virtual Error serialize(Assembler* assembler) = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Internal assembler.
Assembler* _assembler;
//! Flow id added to each node created (used only by `Context)`.
uint32_t _nodeFlowId;
//! Flags added to each node created (used only by `Context)`.
uint32_t _nodeFlags;
//! Maximum count of nodes to look ahead when allocating/spilling
//! registers.
uint32_t _maxLookAhead;
//! Variable mapping (translates incoming VarType into target).
const uint8_t* _targetVarMapping;
//! First node.
Node* _firstNode;
//! Last node.
Node* _lastNode;
//! Current node.
Node* _cursor;
//! Current function.
FuncNode* _func;
//! Variable zone.
Zone _varZone;
//! String/data zone.
Zone _stringZone;
//! Local constant pool zone.
Zone _localConstZone;
//! TargetNode list.
PodVector<TargetNode*> _targetList;
//! VarData list.
PodVector<VarData*> _varList;
//! Local constant pool, flushed at the end of each function.
ConstPool _localConstPool;
//! Global constant pool, flushed at the end of the compilation.
ConstPool _globalConstPool;
//! Label to start of the local constant pool.
Label _localConstPoolLabel;
//! Label to start of the global constant pool.
Label _globalConstPoolLabel;
};
//! \}
// ============================================================================
// [Defined-Later]
// ============================================================================
ASMJIT_INLINE Label::Label(Compiler& c) : Operand(NoInit) {
c._newLabel(this);
}
ASMJIT_INLINE Node::Node(Compiler* compiler, uint32_t type) {
_prev = NULL;
_next = NULL;
_type = static_cast<uint8_t>(type);
_opCount = 0;
_flags = static_cast<uint16_t>(compiler->_nodeFlags);
_flowId = compiler->_nodeFlowId;
_comment = NULL;
_map = NULL;
_liveness = NULL;
_state = NULL;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
#endif // _ASMJIT_BASE_COMPILER_H
......@@ -7,12 +7,14 @@
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
// [Dependencies]
#include "../base/constpool.h"
#include "../base/intutil.h"
#include "../base/utils.h"
#include <algorithm>
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
......@@ -21,17 +23,17 @@ namespace asmjit {
// get, insert and traverse.
// ============================================================================
// [asmjit::ConstPoolTree - Ops]
// [asmjit::ConstPool::Tree - Ops]
// ============================================================================
//! \internal
//!
//! Remove left horizontal links.
static ASMJIT_INLINE ConstPoolNode* ConstPoolTree_skewNode(ConstPoolNode* node) {
ConstPoolNode* link = node->_link[0];
static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_skewNode(ConstPool::Node* node) noexcept {
ConstPool::Node* link = node->_link[0];
uint32_t level = node->_level;
if (level != 0 && link != NULL && link->_level == level) {
if (level != 0 && link && link->_level == level) {
node->_link[0] = link->_link[1];
link->_link[1] = node;
......@@ -44,11 +46,11 @@ static ASMJIT_INLINE ConstPoolNode* ConstPoolTree_skewNode(ConstPoolNode* node)
//! \internal
//!
//! Remove consecutive horizontal links.
static ASMJIT_INLINE ConstPoolNode* ConstPoolTree_splitNode(ConstPoolNode* node) {
ConstPoolNode* link = node->_link[1];
static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_splitNode(ConstPool::Node* node) noexcept {
ConstPool::Node* link = node->_link[1];
uint32_t level = node->_level;
if (level != 0 && link != NULL && link->_link[1] != NULL && link->_link[1]->_level == level) {
if (level != 0 && link && link->_link[1] && link->_link[1]->_level == level) {
node->_link[1] = link->_link[0];
link->_link[0] = node;
......@@ -59,31 +61,31 @@ static ASMJIT_INLINE ConstPoolNode* ConstPoolTree_splitNode(ConstPoolNode* node)
return node;
}
ConstPoolNode* ConstPoolTree::get(const void* data) {
ConstPoolNode* node = _root;
ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept {
ConstPool::Node* node = _root;
size_t dataSize = _dataSize;
while (node != NULL) {
while (node) {
int c = ::memcmp(node->getData(), data, dataSize);
if (c == 0)
return node;
node = node->_link[c < 0];
}
return NULL;
return nullptr;
}
void ConstPoolTree::put(ConstPoolNode* newNode) {
void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept {
size_t dataSize = _dataSize;
_length++;
if (_root == NULL) {
if (!_root) {
_root = newNode;
return;
}
ConstPoolNode* node = _root;
ConstPoolNode* stack[kHeightLimit];
ConstPool::Node* node = _root;
ConstPool::Node* stack[kHeightLimit];
unsigned int top = 0;
unsigned int dir;
......@@ -93,9 +95,8 @@ void ConstPoolTree::put(ConstPoolNode* newNode) {
stack[top++] = node;
dir = ::memcmp(node->getData(), newNode->getData(), dataSize) < 0;
ConstPoolNode* link = node->_link[dir];
if (link == NULL)
break;
ConstPool::Node* link = node->_link[dir];
if (!link) break;
node = link;
}
......@@ -126,34 +127,25 @@ void ConstPoolTree::put(ConstPoolNode* newNode) {
// [asmjit::ConstPool - Construction / Destruction]
// ============================================================================
ConstPool::ConstPool(Zone* zone) {
_zone = zone;
size_t dataSize = 1;
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
_tree[i].setDataSize(dataSize);
_gaps[i] = NULL;
dataSize <<= 1;
}
_gapPool = NULL;
_size = 0;
_alignment = 0;
}
ConstPool::~ConstPool() {}
ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); }
ConstPool::~ConstPool() noexcept {}
// ============================================================================
// [asmjit::ConstPool - Reset]
// ============================================================================
void ConstPool::reset() {
void ConstPool::reset(Zone* zone) noexcept {
_zone = zone;
size_t dataSize = 1;
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) {
_tree[i].reset();
_gaps[i] = NULL;
_tree[i].setDataSize(dataSize);
_gaps[i] = nullptr;
dataSize <<= 1;
}
_gapPool = NULL;
_gapPool = nullptr;
_size = 0;
_alignment = 0;
}
......@@ -162,40 +154,39 @@ void ConstPool::reset() {
// [asmjit::ConstPool - Ops]
// ============================================================================
static ASMJIT_INLINE ConstPoolGap* ConstPool_allocGap(ConstPool* self) {
ConstPoolGap* gap = self->_gapPool;
if (gap == NULL)
return self->_zone->allocT<ConstPoolGap>();
static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept {
ConstPool::Gap* gap = self->_gapPool;
if (!gap) return self->_zone->allocT<ConstPool::Gap>();
self->_gapPool = gap->_next;
return gap;
}
static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self, ConstPoolGap* gap) {
static ASMJIT_INLINE void ConstPool_freeGap(ConstPool* self, ConstPool::Gap* gap) noexcept {
gap->_next = self->_gapPool;
self->_gapPool = gap;
}
static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) {
static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noexcept {
ASMJIT_ASSERT(length > 0);
while (length > 0) {
size_t gapIndex;
size_t gapLength;
if (length >= 16 && IntUtil::isAligned<size_t>(offset, 16)) {
gapIndex = ConstPool::kIndex16;
if (length >= 16 && Utils::isAligned<size_t>(offset, 16)) {
gapLength = 16;
}
else if (length >= 8 && IntUtil::isAligned<size_t>(offset, 8)) {
else if (length >= 8 && Utils::isAligned<size_t>(offset, 8)) {
gapIndex = ConstPool::kIndex8;
gapLength = 8;
}
else if (length >= 4 && IntUtil::isAligned<size_t>(offset, 4)) {
else if (length >= 4 && Utils::isAligned<size_t>(offset, 4)) {
gapIndex = ConstPool::kIndex4;
gapLength = 4;
}
else if (length >= 2 && IntUtil::isAligned<size_t>(offset, 2)) {
else if (length >= 2 && Utils::isAligned<size_t>(offset, 2)) {
gapIndex = ConstPool::kIndex2;
gapLength = 2;
}
......@@ -207,9 +198,8 @@ static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) {
// We don't have to check for errors here, if this failed nothing really
// happened (just the gap won't be visible) and it will fail again at
// place where checking will cause kErrorNoHeapMemory.
ConstPoolGap* gap = ConstPool_allocGap(self);
if (gap == NULL)
return;
ConstPool::Gap* gap = ConstPool_allocGap(self);
if (!gap) return;
gap->_next = self->_gaps[gapIndex];
self->_gaps[gapIndex] = gap;
......@@ -222,7 +212,7 @@ static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) {
}
}
Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept {
size_t treeIndex;
if (size == 32)
......@@ -238,10 +228,10 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
else if (size == 1)
treeIndex = kIndex1;
else
return kErrorInvalidArgument;
return DebugUtils::errored(kErrorInvalidArgument);
ConstPoolNode* node = _tree[treeIndex].get(data);
if (node != NULL) {
ConstPool::Node* node = _tree[treeIndex].get(data);
if (node) {
dstOffset = node->_offset;
return kErrorOk;
}
......@@ -252,10 +242,10 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
size_t gapIndex = treeIndex;
while (gapIndex != kIndexCount - 1) {
ConstPoolGap* gap = _gaps[treeIndex];
ConstPool::Gap* gap = _gaps[treeIndex];
// Check if there is a gap.
if (gap != NULL) {
if (gap) {
size_t gapOffset = gap->_offset;
size_t gapLength = gap->_length;
......@@ -264,7 +254,7 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
ConstPool_freeGap(this, gap);
offset = gapOffset;
ASMJIT_ASSERT(IntUtil::isAligned<size_t>(offset, size));
ASMJIT_ASSERT(Utils::isAligned<size_t>(offset, size));
gapLength -= size;
if (gapLength > 0)
......@@ -277,11 +267,11 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
if (offset == ~static_cast<size_t>(0)) {
// Get how many bytes have to be skipped so the address is aligned accordingly
// to the 'size'.
size_t deltaTo = IntUtil::deltaTo<size_t>(_size, size);
size_t diff = Utils::alignDiff<size_t>(_size, size);
if (deltaTo != 0) {
ConstPool_addGap(this, _size, deltaTo);
_size += deltaTo;
if (diff != 0) {
ConstPool_addGap(this, _size, diff);
_size += diff;
}
offset = _size;
......@@ -289,12 +279,11 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
}
// Add the initial node to the right index.
node = ConstPoolTree::_newNode(_zone, data, size, offset, false);
if (node == NULL)
return kErrorNoHeapMemory;
node = ConstPool::Tree::_newNode(_zone, data, size, offset, false);
if (!node) return DebugUtils::errored(kErrorNoHeapMemory);
_tree[treeIndex].put(node);
_alignment = IntUtil::iMax<size_t>(_alignment, size);
_alignment = std::max<size_t>(_alignment, size);
dstOffset = offset;
......@@ -312,11 +301,9 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
const uint8_t* pData = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < pCount; i++, pData += size) {
node = _tree[treeIndex].get(pData);
if (node) continue;
if (node != NULL)
continue;
node = ConstPoolTree::_newNode(_zone, pData, size, offset + (i * size), true);
node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true);
_tree[treeIndex].put(node);
}
}
......@@ -329,11 +316,11 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) {
// ============================================================================
struct ConstPoolFill {
ASMJIT_INLINE ConstPoolFill(uint8_t* dst, size_t dataSize) :
ASMJIT_INLINE ConstPoolFill(uint8_t* dst, size_t dataSize) noexcept :
_dst(dst),
_dataSize(dataSize) {}
ASMJIT_INLINE void visit(const ConstPoolNode* node) {
ASMJIT_INLINE void visit(const ConstPool::Node* node) noexcept {
if (!node->_shared)
::memcpy(_dst + node->_offset, node->getData(), _dataSize);
}
......@@ -342,7 +329,7 @@ struct ConstPoolFill {
size_t _dataSize;
};
void ConstPool::fill(void* dst) {
void ConstPool::fill(void* dst) const noexcept {
// Clears possible gaps, asmjit should never emit garbage to the output.
::memset(dst, 0, _size);
......@@ -359,7 +346,7 @@ void ConstPool::fill(void* dst) {
#if defined(ASMJIT_TEST)
UNIT(base_constpool) {
Zone zone(32384 - kZoneOverhead);
Zone zone(32384 - Zone::kZoneOverhead);
ConstPool pool(&zone);
uint32_t i;
......@@ -372,23 +359,23 @@ UNIT(base_constpool) {
uint64_t c = ASMJIT_UINT64_C(0x0101010101010101);
EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(prevOffset == 0,
"pool.add() - First constant should have zero offset.");
"pool.add() - First constant should have zero offset");
for (i = 1; i < kCount; i++) {
c++;
EXPECT(pool.add(&c, 8, curOffset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(prevOffset + 8 == curOffset,
"pool.add() - Returned incorrect curOffset.");
"pool.add() - Returned incorrect curOffset");
EXPECT(pool.getSize() == (i + 1) * 8,
"pool.getSize() - Reported incorrect size.");
"pool.getSize() - Reported incorrect size");
prevOffset = curOffset;
}
EXPECT(pool.getAlignment() == 8,
"pool.getAlignment() - Expected 8-byte alignment.");
"pool.getAlignment() - Expected 8-byte alignment");
}
INFO("Retrieving %u constants from the pool.", kCount);
......@@ -398,74 +385,75 @@ UNIT(base_constpool) {
for (i = 0; i < kCount; i++) {
size_t offset;
EXPECT(pool.add(&c, 8, offset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(offset == i * 8,
"pool.add() - Should have reused constant.");
"pool.add() - Should have reused constant");
c++;
}
}
INFO("Checking if the constants were split into 4-byte patterns.");
INFO("Checking if the constants were split into 4-byte patterns");
{
uint32_t c = 0x01010101;
for (i = 0; i < kCount; i++) {
size_t offset;
EXPECT(pool.add(&c, 4, offset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(offset == i * 8,
"pool.add() - Should reuse existing constant.");
"pool.add() - Should reuse existing constant");
c++;
}
}
INFO("Adding 2 byte constant to misalign the current offset.");
INFO("Adding 2 byte constant to misalign the current offset");
{
uint16_t c = 0xFFFF;
size_t offset;
EXPECT(pool.add(&c, 2, offset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(offset == kCount * 8,
"pool.add() - Didn't return expected position.");
"pool.add() - Didn't return expected position");
EXPECT(pool.getAlignment() == 8,
"pool.getAlignment() - Expected 8-byte alignment.");
"pool.getAlignment() - Expected 8-byte alignment");
}
INFO("Adding 8 byte constant to check if pool gets aligned again.");
INFO("Adding 8 byte constant to check if pool gets aligned again");
{
uint64_t c = ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF);
size_t offset;
EXPECT(pool.add(&c, 8, offset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(offset == kCount * 8 + 8,
"pool.add() - Didn't return aligned offset.");
"pool.add() - Didn't return aligned offset");
}
INFO("Adding 2 byte constant to verify the gap is filled.");
INFO("Adding 2 byte constant to verify the gap is filled");
{
uint16_t c = 0xFFFE;
size_t offset;
EXPECT(pool.add(&c, 2, offset) == kErrorOk,
"pool.add() - Returned error.");
"pool.add() - Returned error");
EXPECT(offset == kCount * 8 + 2,
"pool.add() - Didn't fill the gap.");
"pool.add() - Didn't fill the gap");
EXPECT(pool.getAlignment() == 8,
"pool.getAlignment() - Expected 8-byte alignment.");
"pool.getAlignment() - Expected 8-byte alignment");
}
INFO("Checking reset functionality.");
INFO("Checking reset functionality");
{
pool.reset();
pool.reset(&zone);
zone.reset();
EXPECT(pool.getSize() == 0,
"pool.getSize() - Expected pool size to be zero.");
"pool.getSize() - Expected pool size to be zero");
EXPECT(pool.getAlignment() == 0,
"pool.getSize() - Expected pool alignment to be zero.");
"pool.getSize() - Expected pool alignment to be zero");
}
INFO("Checking pool alignment when combined constants are added.");
INFO("Checking pool alignment when combined constants are added");
{
uint8_t bytes[32] = { 0 };
size_t offset;
......@@ -473,46 +461,46 @@ UNIT(base_constpool) {
pool.add(bytes, 1, offset);
EXPECT(pool.getSize() == 1,
"pool.getSize() - Expected pool size to be 1 byte.");
"pool.getSize() - Expected pool size to be 1 byte");
EXPECT(pool.getAlignment() == 1,
"pool.getSize() - Expected pool alignment to be 1 byte.");
"pool.getSize() - Expected pool alignment to be 1 byte");
EXPECT(offset == 0,
"pool.getSize() - Expected offset returned to be zero.");
"pool.getSize() - Expected offset returned to be zero");
pool.add(bytes, 2, offset);
EXPECT(pool.getSize() == 4,
"pool.getSize() - Expected pool size to be 4 bytes.");
"pool.getSize() - Expected pool size to be 4 bytes");
EXPECT(pool.getAlignment() == 2,
"pool.getSize() - Expected pool alignment to be 2 bytes.");
"pool.getSize() - Expected pool alignment to be 2 bytes");
EXPECT(offset == 2,
"pool.getSize() - Expected offset returned to be 2.");
"pool.getSize() - Expected offset returned to be 2");
pool.add(bytes, 4, offset);
EXPECT(pool.getSize() == 8,
"pool.getSize() - Expected pool size to be 8 bytes.");
"pool.getSize() - Expected pool size to be 8 bytes");
EXPECT(pool.getAlignment() == 4,
"pool.getSize() - Expected pool alignment to be 4 bytes.");
"pool.getSize() - Expected pool alignment to be 4 bytes");
EXPECT(offset == 4,
"pool.getSize() - Expected offset returned to be 4.");
"pool.getSize() - Expected offset returned to be 4");
pool.add(bytes, 4, offset);
EXPECT(pool.getSize() == 8,
"pool.getSize() - Expected pool size to be 8 bytes.");
"pool.getSize() - Expected pool size to be 8 bytes");
EXPECT(pool.getAlignment() == 4,
"pool.getSize() - Expected pool alignment to be 4 bytes.");
"pool.getSize() - Expected pool alignment to be 4 bytes");
EXPECT(offset == 4,
"pool.getSize() - Expected offset returned to be 8.");
"pool.getSize() - Expected offset returned to be 8");
pool.add(bytes, 32, offset);
EXPECT(pool.getSize() == 64,
"pool.getSize() - Expected pool size to be 64 bytes.");
"pool.getSize() - Expected pool size to be 64 bytes");
EXPECT(pool.getAlignment() == 32,
"pool.getSize() - Expected pool alignment to be 32 bytes.");
"pool.getSize() - Expected pool alignment to be 32 bytes");
EXPECT(offset == 32,
"pool.getSize() - Expected offset returned to be 32.");
"pool.getSize() - Expected offset returned to be 32");
}
}
#endif // ASMJIT_TEST
......@@ -520,4 +508,4 @@ UNIT(base_constpool) {
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
......@@ -8,244 +8,204 @@
#ifndef _ASMJIT_BASE_CONSTPOOL_H
#define _ASMJIT_BASE_CONSTPOOL_H
// [Dependencies - AsmJit]
#include "../base/error.h"
// [Dependencies]
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::ConstPoolNode]
// [asmjit::ConstPool]
// ============================================================================
//! \internal
//!
//! Zone-allocated constant-pool node.
struct ConstPoolNode {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE void* getData() const {
return static_cast<void*>(const_cast<ConstPoolNode*>(this) + 1);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Left/Right nodes.
ConstPoolNode* _link[2];
//! Horizontal level for balance.
uint32_t _level : 31;
//! Whether this constant is shared with another.
uint32_t _shared : 1;
//! Data offset from the beginning of the pool.
uint32_t _offset;
};
// ============================================================================
// [asmjit::ConstPoolTree]
// ============================================================================
//! Constant pool.
class ConstPool {
public:
ASMJIT_NONCOPYABLE(ConstPool)
//! \internal
//!
//! Zone-allocated constant-pool tree.
struct ConstPoolTree {
enum {
//! Maximum tree height == log2(1 << 64).
kHeightLimit = 64
kIndex1 = 0,
kIndex2 = 1,
kIndex4 = 2,
kIndex8 = 3,
kIndex16 = 4,
kIndex32 = 5,
kIndexCount = 6
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// [Gap]
// --------------------------------------------------------------------------
ASMJIT_INLINE ConstPoolTree(size_t dataSize = 0) :
_root(NULL),
_length(0),
_dataSize(dataSize) {}
ASMJIT_INLINE ~ConstPoolTree() {}
//! \internal
//!
//! Zone-allocated const-pool gap.
struct Gap {
Gap* _next; //!< Pointer to the next gap
size_t _offset; //!< Offset of the gap.
size_t _length; //!< Remaining bytes of the gap (basically a gap size).
};
// --------------------------------------------------------------------------
// [Reset]
// [Node]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
_root = NULL;
_length = 0;
}
//! \internal
//!
//! Zone-allocated const-pool node.
struct Node {
ASMJIT_INLINE void* getData() const noexcept {
return static_cast<void*>(const_cast<ConstPool::Node*>(this) + 1);
}
Node* _link[2]; //!< Left/Right nodes.
uint32_t _level : 31; //!< Horizontal level for balance.
uint32_t _shared : 1; //!< If this constant is shared with another.
uint32_t _offset; //!< Data offset from the beginning of the pool.
};
// --------------------------------------------------------------------------
// [Accessors]
// [Tree]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isEmpty() const {
return _length == 0;
}
//! \internal
//!
//! Zone-allocated const-pool tree.
struct Tree {
enum {
//! Maximum tree height == log2(1 << 64).
kHeightLimit = 64
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE Tree(size_t dataSize = 0) noexcept
: _root(nullptr),
_length(0),
_dataSize(dataSize) {}
ASMJIT_INLINE ~Tree() {}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() noexcept {
_root = nullptr;
_length = 0;
}
ASMJIT_INLINE size_t getLength() const {
return _length;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setDataSize(size_t dataSize) {
ASMJIT_ASSERT(isEmpty());
_dataSize = dataSize;
}
ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; }
ASMJIT_INLINE size_t getLength() const noexcept { return _length; }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setDataSize(size_t dataSize) noexcept {
ASMJIT_ASSERT(isEmpty());
_dataSize = dataSize;
}
ASMJIT_API ConstPoolNode* get(const void* data);
ASMJIT_API void put(ConstPoolNode* node);
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// [Iterate]
// --------------------------------------------------------------------------
template<typename Visitor>
ASMJIT_INLINE void iterate(Visitor& visitor) const {
ConstPoolNode* node = const_cast<ConstPoolNode*>(_root);
ConstPoolNode* link;
ASMJIT_API Node* get(const void* data) noexcept;
ASMJIT_API void put(Node* node) noexcept;
ConstPoolNode* stack[kHeightLimit];
// --------------------------------------------------------------------------
// [Iterate]
// --------------------------------------------------------------------------
if (node == NULL)
return;
template<typename Visitor>
ASMJIT_INLINE void iterate(Visitor& visitor) const noexcept {
Node* node = const_cast<Node*>(_root);
if (!node) return;
size_t top = 0;
Node* stack[kHeightLimit];
size_t top = 0;
for (;;) {
link = node->_link[0];
for (;;) {
Node* left = node->_link[0];
if (left != nullptr) {
ASMJIT_ASSERT(top != kHeightLimit);
stack[top++] = node;
if (link != NULL) {
ASMJIT_ASSERT(top != kHeightLimit);
stack[top++] = node;
node = left;
continue;
}
node = link;
continue;
}
Visit:
visitor.visit(node);
node = node->_link[1];
if (node != nullptr)
continue;
_Visit:
visitor.visit(node);
link = node->_link[1];
if (top == 0)
return;
if (link != NULL) {
node = link;
continue;
node = stack[--top];
goto Visit;
}
if (top == 0)
break;
node = stack[--top];
goto _Visit;
}
}
// --------------------------------------------------------------------------
// [Helpers]
// --------------------------------------------------------------------------
static ASMJIT_INLINE ConstPoolNode* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) {
ConstPoolNode* node = zone->allocT<ConstPoolNode>(sizeof(ConstPoolNode) + size);
if (node == NULL)
return NULL;
node->_link[0] = NULL;
node->_link[1] = NULL;
node->_level = 1;
node->_shared = shared;
node->_offset = static_cast<uint32_t>(offset);
// --------------------------------------------------------------------------
// [Helpers]
// --------------------------------------------------------------------------
::memcpy(node->getData(), data, size);
return node;
}
static ASMJIT_INLINE Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept {
Node* node = zone->allocT<Node>(sizeof(Node) + size);
if (ASMJIT_UNLIKELY(!node)) return nullptr;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Root of the tree
ConstPoolNode* _root;
//! Length of the tree (count of nodes).
size_t _length;
//! Size of the data.
size_t _dataSize;
};
node->_link[0] = nullptr;
node->_link[1] = nullptr;
node->_level = 1;
node->_shared = shared;
node->_offset = static_cast<uint32_t>(offset);
// ============================================================================
// [asmjit::ConstPoolGap]
// ============================================================================
//! \internal
//!
//! Zone-allocated constant-pool gap.
struct ConstPoolGap {
//! Link to the next gap
ConstPoolGap* _next;
//! Offset of the gap.
size_t _offset;
//! Remaining bytes of the gap (basically a gap size).
size_t _length;
};
::memcpy(node->getData(), data, size);
return node;
}
// ============================================================================
// [asmjit::ConstPool]
// ============================================================================
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Constant pool.
struct ConstPool {
ASMJIT_NO_COPY(ConstPool)
enum {
kIndex1 = 0,
kIndex2 = 1,
kIndex4 = 2,
kIndex8 = 3,
kIndex16 = 4,
kIndex32 = 5,
kIndexCount = 6
Node* _root; //!< Root of the tree
size_t _length; //!< Length of the tree (count of nodes).
size_t _dataSize; //!< Size of the data.
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_API ConstPool(Zone* zone);
ASMJIT_API ~ConstPool();
ASMJIT_API ConstPool(Zone* zone) noexcept;
ASMJIT_API ~ConstPool() noexcept;
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_API void reset();
ASMJIT_API void reset(Zone* zone) noexcept;
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Get whether the constant-pool is empty.
ASMJIT_INLINE bool isEmpty() const {
return _size == 0;
}
ASMJIT_INLINE bool isEmpty() const noexcept { return _size == 0; }
//! Get the size of the constant-pool in bytes.
ASMJIT_INLINE size_t getSize() const {
return _size;
}
ASMJIT_INLINE size_t getSize() const noexcept { return _size; }
//! Get minimum alignment.
ASMJIT_INLINE size_t getAlignment() const {
return _alignment;
}
ASMJIT_INLINE size_t getAlignment() const noexcept { return _alignment; }
//! Add a constant to the constant pool.
//!
......@@ -264,32 +224,26 @@ struct ConstPool {
//! been already added. For example if you try to add 4-byte constant and then
//! 8-byte constant having the same 4-byte pattern as the previous one, two
//! independent slots will be generated by the pool.
ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset);
ASMJIT_API Error add(const void* data, size_t size, size_t& dstOffset) noexcept;
// --------------------------------------------------------------------------
// [Fill]
// --------------------------------------------------------------------------
//! Fill the destination with the constants from the pool.
ASMJIT_API void fill(void* dst);
ASMJIT_API void fill(void* dst) const noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Zone allocator.
Zone* _zone;
//! Tree per size.
ConstPoolTree _tree[kIndexCount];
//! Gaps per size.
ConstPoolGap* _gaps[kIndexCount];
//! Gaps pool
ConstPoolGap* _gapPool;
//! Size of the pool (in bytes).
size_t _size;
//! Alignemnt.
size_t _alignment;
Zone* _zone; //!< Zone allocator.
Tree _tree[kIndexCount]; //!< Tree per size.
Gap* _gaps[kIndexCount]; //!< Gaps per size.
Gap* _gapPool; //!< Gaps pool
size_t _size; //!< Size of the pool (in bytes).
size_t _alignment; //!< Required pool alignment.
};
//! \}
......@@ -297,7 +251,7 @@ struct ConstPool {
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CONSTPOOL_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/containers.h"
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::PodVectorBase - NullData]
// ============================================================================
const PodVectorData PodVectorBase::_nullData = { 0, 0 };
// ============================================================================
// [asmjit::PodVectorBase - Reset]
// ============================================================================
//! Clear vector data and free internal buffer.
void PodVectorBase::reset(bool releaseMemory) {
PodVectorData* d = _d;
if (d == &_nullData)
return;
if (releaseMemory) {
ASMJIT_FREE(d);
_d = const_cast<PodVectorData*>(&_nullData);
return;
}
d->length = 0;
}
// ============================================================================
// [asmjit::PodVectorBase - Helpers]
// ============================================================================
Error PodVectorBase::_grow(size_t n, size_t sizeOfT) {
PodVectorData* d = _d;
size_t threshold = kMemAllocGrowMax / sizeOfT;
size_t capacity = d->capacity;
size_t after = d->length;
if (IntUtil::maxUInt<size_t>() - n < after)
return kErrorNoHeapMemory;
after += n;
if (capacity >= after)
return kErrorOk;
// PodVector is used as a linear array for some data structures used by
// AsmJit code generation. The purpose of this agressive growing schema
// is to minimize memory reallocations, because AsmJit code generation
// classes live short life and will be freed or reused soon.
if (capacity < 32)
capacity = 32;
else if (capacity < 128)
capacity = 128;
else if (capacity < 512)
capacity = 512;
while (capacity < after) {
if (capacity < threshold)
capacity *= 2;
else
capacity += threshold;
}
return _reserve(capacity, sizeOfT);
}
Error PodVectorBase::_reserve(size_t n, size_t sizeOfT) {
PodVectorData* d = _d;
if (d->capacity >= n)
return kErrorOk;
size_t nBytes = sizeof(PodVectorData) + n * sizeOfT;
if (nBytes < n)
return kErrorNoHeapMemory;
if (d == &_nullData) {
d = static_cast<PodVectorData*>(ASMJIT_ALLOC(nBytes));
if (d == NULL)
return kErrorNoHeapMemory;
d->length = 0;
}
else {
d = static_cast<PodVectorData*>(ASMJIT_REALLOC(d, nBytes));
if (d == NULL)
return kErrorNoHeapMemory;
}
d->capacity = n;
_d = d;
return kErrorOk;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CONTAINERS_H
#define _ASMJIT_BASE_CONTAINERS_H
// [Dependencies - AsmJit]
#include "../base/error.h"
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::PodVectorData]
// ============================================================================
//! \internal
struct PodVectorData {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get data.
ASMJIT_INLINE void* getData() const {
return (void*)(this + 1);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Capacity of the vector.
size_t capacity;
//! Length of the vector.
size_t length;
};
// ============================================================================
// [asmjit::PodVectorBase]
// ============================================================================
//! \internal
struct PodVectorBase {
static ASMJIT_API const PodVectorData _nullData;
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new instance of `PodVectorBase`.
ASMJIT_INLINE PodVectorBase() :
_d(const_cast<PodVectorData*>(&_nullData)) {}
//! Destroy the `PodVectorBase` and data.
ASMJIT_INLINE ~PodVectorBase() {
reset(true);
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the vector data and set its `length` to zero.
//!
//! If `releaseMemory` is true the vector buffer will be released to the
//! system.
ASMJIT_API void reset(bool releaseMemory = false);
// --------------------------------------------------------------------------
// [Grow / Reserve]
// --------------------------------------------------------------------------
protected:
ASMJIT_API Error _grow(size_t n, size_t sizeOfT);
ASMJIT_API Error _reserve(size_t n, size_t sizeOfT);
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
public:
PodVectorData* _d;
};
// ============================================================================
// [asmjit::PodVector<T>]
// ============================================================================
//! Template used to store and manage array of POD data.
//!
//! This template has these adventages over other vector<> templates:
//! - Non-copyable (designed to be non-copyable, we want it)
//! - No copy-on-write (some implementations of stl can use it)
//! - Optimized for working only with POD types
//! - Uses ASMJIT_... memory management macros
template <typename T>
struct PodVector : PodVectorBase {
ASMJIT_NO_COPY(PodVector<T>)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new instance of `PodVector<T>`.
ASMJIT_INLINE PodVector() {}
//! Destroy the `PodVector<>` and data.
ASMJIT_INLINE ~PodVector() {}
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
//! Get whether the vector is empty.
ASMJIT_INLINE bool isEmpty() const {
return _d->length == 0;
}
//! Get length.
ASMJIT_INLINE size_t getLength() const {
return _d->length;
}
//! Get capacity.
ASMJIT_INLINE size_t getCapacity() const {
return _d->capacity;
}
//! Get data.
ASMJIT_INLINE T* getData() {
return static_cast<T*>(_d->getData());
}
//! \overload
ASMJIT_INLINE const T* getData() const {
return static_cast<const T*>(_d->getData());
}
// --------------------------------------------------------------------------
// [Grow / Reserve]
// --------------------------------------------------------------------------
//! Called to grow the buffer to fit at least `n` elements more.
ASMJIT_INLINE Error _grow(size_t n) {
return PodVectorBase::_grow(n, sizeof(T));
}
//! Realloc internal array to fit at least `n` items.
ASMJIT_INLINE Error _reserve(size_t n) {
return PodVectorBase::_reserve(n, sizeof(T));
}
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Prepend `item` to vector.
Error prepend(const T& item) {
PodVectorData* d = _d;
if (d->length == d->capacity) {
ASMJIT_PROPAGATE_ERROR(_grow(1));
_d = d;
}
::memmove(static_cast<T*>(d->getData()) + 1, d->getData(), d->length * sizeof(T));
::memcpy(d->getData(), &item, sizeof(T));
d->length++;
return kErrorOk;
}
//! Insert an `item` at the `index`.
Error insert(size_t index, const T& item) {
PodVectorData* d = _d;
ASMJIT_ASSERT(index <= d->length);
if (d->length == d->capacity) {
ASMJIT_PROPAGATE_ERROR(_grow(1));
d = _d;
}
T* dst = static_cast<T*>(d->getData()) + index;
::memmove(dst + 1, dst, d->length - index);
::memcpy(dst, &item, sizeof(T));
d->length++;
return kErrorOk;
}
//! Append `item` to vector.
Error append(const T& item) {
PodVectorData* d = _d;
if (d->length == d->capacity) {
ASMJIT_PROPAGATE_ERROR(_grow(1));
d = _d;
}
::memcpy(static_cast<T*>(d->getData()) + d->length, &item, sizeof(T));
d->length++;
return kErrorOk;
}
//! Get index of `val` or `kInvalidIndex` if not found.
size_t indexOf(const T& val) const {
PodVectorData* d = _d;
const T* data = static_cast<const T*>(d->getData());
size_t len = d->length;
for (size_t i = 0; i < len; i++)
if (data[i] == val)
return i;
return kInvalidIndex;
}
//! Remove item at index `i`.
void removeAt(size_t i) {
PodVectorData* d = _d;
ASMJIT_ASSERT(i < d->length);
T* data = static_cast<T*>(d->getData()) + i;
d->length--;
::memmove(data, data + 1, d->length - i);
}
//! Swap this pod-vector with `other`.
void swap(PodVector<T>& other) {
T* otherData = other._d;
other._d = _d;
_d = otherData;
}
//! Get item at index `i`.
ASMJIT_INLINE T& operator[](size_t i) {
ASMJIT_ASSERT(i < getLength());
return getData()[i];
}
//! Get item at index `i`.
ASMJIT_INLINE const T& operator[](size_t i) const {
ASMJIT_ASSERT(i < getLength());
return getData()[i];
}
};
// ============================================================================
// [asmjit::PodList<T>]
// ============================================================================
//! \internal
template <typename T>
struct PodList {
ASMJIT_NO_COPY(PodList<T>)
// --------------------------------------------------------------------------
// [Link]
// --------------------------------------------------------------------------
struct Link {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get next node.
ASMJIT_INLINE Link* getNext() const { return _next; }
//! Get value.
ASMJIT_INLINE T getValue() const { return _value; }
//! Set value to `value`.
ASMJIT_INLINE void setValue(const T& value) { _value = value; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
Link* _next;
T _value;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE PodList() : _first(NULL), _last(NULL) {}
ASMJIT_INLINE ~PodList() {}
// --------------------------------------------------------------------------
// [Data]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isEmpty() const { return _first != NULL; }
ASMJIT_INLINE Link* getFirst() const { return _first; }
ASMJIT_INLINE Link* getLast() const { return _last; }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
_first = NULL;
_last = NULL;
}
ASMJIT_INLINE void prepend(Link* link) {
link->_next = _first;
if (_first == NULL)
_last = link;
_first = link;
}
ASMJIT_INLINE void append(Link* link) {
link->_next = NULL;
if (_first == NULL)
_first = link;
else
_last->_next = link;
_last = link;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
Link* _first;
Link* _last;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CONTAINERS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CONTEXT_P_H
#define _ASMJIT_BASE_CONTEXT_P_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_COMPILER)
// [Dependencies - AsmJit]
#include "../base/compiler.h"
#include "../base/zone.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_compiler
//! \{
// ============================================================================
// [asmjit::Context]
// ============================================================================
//! \internal
//!
//! Code generation context is the logic behind `Compiler`. The context is
//! used to compile the code stored in `Compiler`.
struct Context {
ASMJIT_NO_COPY(Context)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
Context(Compiler* compiler);
virtual ~Context();
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the whole context.
virtual void reset(bool releaseMemory = false);
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get compiler.
ASMJIT_INLINE Compiler* getCompiler() const { return _compiler; }
//! Get function.
ASMJIT_INLINE FuncNode* getFunc() const { return _func; }
//! Get stop node.
ASMJIT_INLINE Node* getStop() const { return _stop; }
//! Get start of the current scope.
ASMJIT_INLINE Node* getStart() const { return _start; }
//! Get end of the current scope.
ASMJIT_INLINE Node* getEnd() const { return _end; }
//! Get extra block.
ASMJIT_INLINE Node* getExtraBlock() const { return _extraBlock; }
//! Set extra block.
ASMJIT_INLINE void setExtraBlock(Node* node) { _extraBlock = node; }
// --------------------------------------------------------------------------
// [Error]
// --------------------------------------------------------------------------
//! Get the last error code.
ASMJIT_INLINE Error getError() const {
return getCompiler()->getError();
}
//! Set the last error code and propagate it through the error handler.
ASMJIT_INLINE Error setError(Error error, const char* message = NULL) {
return getCompiler()->setError(error, message);
}
// --------------------------------------------------------------------------
// [State]
// --------------------------------------------------------------------------
//! Get current state.
ASMJIT_INLINE VarState* getState() const {
return _state;
}
//! Load current state from `target` state.
virtual void loadState(VarState* src) = 0;
//! Save current state, returning new `VarState` instance.
virtual VarState* saveState() = 0;
//! Change the current state to `target` state.
virtual void switchState(VarState* src) = 0;
//! Change the current state to the intersection of two states `a` and `b`.
virtual void intersectStates(VarState* a, VarState* b) = 0;
// --------------------------------------------------------------------------
// [Context]
// --------------------------------------------------------------------------
ASMJIT_INLINE Error _registerContextVar(VarData* vd) {
if (vd->hasContextId())
return kErrorOk;
uint32_t cid = static_cast<uint32_t>(_contextVd.getLength());
ASMJIT_PROPAGATE_ERROR(_contextVd.append(vd));
vd->setContextId(cid);
return kErrorOk;
}
// --------------------------------------------------------------------------
// [Mem]
// --------------------------------------------------------------------------
MemCell* _newVarCell(VarData* vd);
MemCell* _newStackCell(uint32_t size, uint32_t alignment);
ASMJIT_INLINE MemCell* getVarCell(VarData* vd) {
MemCell* cell = vd->getMemCell();
return cell ? cell : _newVarCell(vd);
}
virtual Error resolveCellOffsets();
// --------------------------------------------------------------------------
// [Bits]
// --------------------------------------------------------------------------
ASMJIT_INLINE VarBits* newBits(uint32_t len) {
return static_cast<VarBits*>(
_baseZone.allocZeroed(static_cast<size_t>(len) * VarBits::kEntitySize));
}
ASMJIT_INLINE VarBits* copyBits(const VarBits* src, uint32_t len) {
return static_cast<VarBits*>(
_baseZone.dup(src, static_cast<size_t>(len) * VarBits::kEntitySize));
}
// --------------------------------------------------------------------------
// [Fetch]
// --------------------------------------------------------------------------
//! Fetch.
//!
//! Fetch iterates over all nodes and gathers information about all variables
//! used. The process generates information required by register allocator,
//! variable liveness analysis and translator.
virtual Error fetch() = 0;
// --------------------------------------------------------------------------
// [RemoveUnreachableCode]
// --------------------------------------------------------------------------
//! Remove unreachable code.
virtual Error removeUnreachableCode();
// --------------------------------------------------------------------------
// [Analyze]
// --------------------------------------------------------------------------
//! Perform variable liveness analysis.
//!
//! Analysis phase iterates over nodes in reverse order and generates a bit
//! array describing variables that are alive at every node in the function.
//! When the analysis start all variables are assumed dead. When a read or
//! read/write operations of a variable is detected the variable becomes
//! alive; when only write operation is detected the variable becomes dead.
//!
//! When a label is found all jumps to that label are followed and analysis
//! repeats until all variables are resolved.
virtual Error livenessAnalysis();
// --------------------------------------------------------------------------
// [Annotate]
// --------------------------------------------------------------------------
virtual Error annotate() = 0;
// --------------------------------------------------------------------------
// [Translate]
// --------------------------------------------------------------------------
//! Translate code by allocating registers and handling state changes.
virtual Error translate() = 0;
// --------------------------------------------------------------------------
// [Schedule]
// --------------------------------------------------------------------------
virtual Error schedule();
// --------------------------------------------------------------------------
// [Cleanup]
// --------------------------------------------------------------------------
virtual void cleanup();
// --------------------------------------------------------------------------
// [Compile]
// --------------------------------------------------------------------------
virtual Error compile(FuncNode* func);
// --------------------------------------------------------------------------
// [Serialize]
// --------------------------------------------------------------------------
virtual Error serialize(Assembler* assembler, Node* start, Node* stop) = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Compiler.
Compiler* _compiler;
//! Function.
FuncNode* _func;
//! Zone allocator.
Zone _baseZone;
//! \internal
//!
//! Offset (how many bytes to add) to `VarMap` to get `VarAttr` array. Used
//! by liveness analysis shared across all backends. This is needed because
//! `VarMap` is a base class for a specialized version that liveness analysis
//! doesn't use, it just needs `VarAttr` array.
uint32_t _varMapToVaListOffset;
//! Start of the current active scope.
Node* _start;
//! End of the current active scope.
Node* _end;
//! Node that is used to insert extra code after the function body.
Node* _extraBlock;
//! Stop node.
Node* _stop;
//! Unreachable nodes.
PodList<Node*> _unreachableList;
//! Jump nodes.
PodList<Node*> _jccList;
//! All variables used by the current function.
PodVector<VarData*> _contextVd;
//! Memory used to spill variables.
MemCell* _memVarCells;
//! Memory used to alloc memory on the stack.
MemCell* _memStackCells;
//! Count of 1-byte cells.
uint32_t _mem1ByteVarsUsed;
//! Count of 2-byte cells.
uint32_t _mem2ByteVarsUsed;
//! Count of 4-byte cells.
uint32_t _mem4ByteVarsUsed;
//! Count of 8-byte cells.
uint32_t _mem8ByteVarsUsed;
//! Count of 16-byte cells.
uint32_t _mem16ByteVarsUsed;
//! Count of 32-byte cells.
uint32_t _mem32ByteVarsUsed;
//! Count of 64-byte cells.
uint32_t _mem64ByteVarsUsed;
//! Count of stack memory cells.
uint32_t _memStackCellsUsed;
//! Maximum memory alignment used by the function.
uint32_t _memMaxAlign;
//! Count of bytes used by variables.
uint32_t _memVarTotal;
//! Count of bytes used by stack.
uint32_t _memStackTotal;
//! Count of bytes used by variables and stack after alignment.
uint32_t _memAllTotal;
//! Default lenght of annotated instruction.
uint32_t _annotationLength;
//! Current state (used by register allocator).
VarState* _state;
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_COMPILER
#endif // _ASMJIT_BASE_CONTEXT_P_H
......@@ -7,73 +7,668 @@
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/utils.h"
#if defined(ASMJIT_ARCH_X86) || defined(ASMJIT_ARCH_X64)
#include "../x86/x86cpuinfo.h"
#else
// ?
#endif
// [Dependencies - Posix]
#if defined(ASMJIT_OS_POSIX)
#if ASMJIT_OS_POSIX
# include <errno.h>
# include <sys/statvfs.h>
# include <sys/utsname.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
# if ASMJIT_CC_MSC_GE(14, 0, 0)
# include <intrin.h> // Required by `__cpuid()` and `_xgetbv()`.
# endif // _MSC_VER >= 1400
#endif
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
# if ASMJIT_OS_LINUX
# include <sys/auxv.h> // Required by `getauxval()`.
# endif
#endif
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CpuInfo - DetectHwThreadsCount]
// [asmjit::CpuInfo - Detect ARM]
// ============================================================================
uint32_t CpuInfo::detectHwThreadsCount() {
#if defined(ASMJIT_OS_WINDOWS)
// ARM information has to be retrieved by the OS (this is how ARM was designed).
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
#if ASMJIT_ARCH_ARM32
static ASMJIT_INLINE void armPopulateBaselineA32Features(CpuInfo* cpuInfo) noexcept {
cpuInfo->_archInfo.init(ArchInfo::kTypeA32);
}
#endif // ASMJIT_ARCH_ARM32
#if ASMJIT_ARCH_ARM64
static ASMJIT_INLINE void armPopulateBaselineA64Features(CpuInfo* cpuInfo) noexcept {
cpuInfo->_archInfo.init(ArchInfo::kTypeA64);
// Thumb (including all variations) is supported on A64 (but not accessible from A64).
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB);
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2);
// A64 is based on ARMv8 and newer.
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
cpuInfo->addFeature(CpuInfo::kArmFeatureV8);
// A64 comes with these features by default.
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv4);
cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP);
cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD);
cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVA);
cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVT);
}
#endif // ASMJIT_ARCH_ARM64
#if ASMJIT_OS_WINDOWS
//! \internal
//!
//! Detect ARM CPU features on Windows.
//!
//! The detection is based on `IsProcessorFeaturePresent()` API call.
static ASMJIT_INLINE void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_ARCH_ARM32
armPopulateBaselineA32Features(cpuInfo);
// Windows for ARM requires at least ARMv7 with DSP extensions.
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP);
// Windows for ARM requires VFPv3.
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3);
// Windows for ARM requires and uses THUMB2.
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB);
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2);
#else
armPopulateBaselineA64Features(cpuInfo);
#endif
// Windows for ARM requires ASIMD.
cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD);
// Detect additional CPU features by calling `IsProcessorFeaturePresent()`.
struct WinPFPMapping {
uint32_t pfpId;
uint32_t featureId;
};
static const WinPFPMapping mapping[] = {
{ PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFPv4 },
{ PF_ARM_VFP_32_REGISTERS_AVAILABLE , CpuInfo::kArmFeatureVFP_D32 },
{ PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIVT },
{ PF_ARM_64BIT_LOADSTORE_ATOMIC , CpuInfo::kArmFeatureAtomics64 }
};
for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(mapping); i++)
if (::IsProcessorFeaturePresent(mapping[i].pfpId))
cpuInfo->addFeature(mapping[i].featureId);
}
#endif // ASMJIT_OS_WINDOWS
#if ASMJIT_OS_LINUX
struct LinuxHWCapMapping {
uint32_t hwcapMask;
uint32_t featureId;
};
static void armDetectHWCaps(CpuInfo* cpuInfo, unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept {
unsigned long mask = getauxval(type);
for (size_t i = 0; i < length; i++)
if ((mask & mapping[i].hwcapMask) == mapping[i].hwcapMask)
cpuInfo->addFeature(mapping[i].featureId);
}
//! \internal
//!
//! Detect ARM CPU features on Linux.
//!
//! The detection is based on `getauxval()`.
ASMJIT_FAVOR_SIZE static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_ARCH_ARM32
armPopulateBaselineA32Features(cpuInfo);
// `AT_HWCAP` provides ARMv7 (and less) related flags.
static const LinuxHWCapMapping hwCapMapping[] = {
{ /* HWCAP_VFP */ (1 << 6), CpuInfo::kArmFeatureVFPv2 },
{ /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureEDSP },
{ /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureASIMD },
{ /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFPv3 },
{ /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFPv4 },
{ /* HWCAP_IDIVA */ (1 << 17), CpuInfo::kArmFeatureIDIVA },
{ /* HWCAP_IDIVT */ (1 << 18), CpuInfo::kArmFeatureIDIVT },
{ /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping));
// VFPv3 implies VFPv2.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2);
}
// VFPv2 implies ARMv6.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv2)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
}
// VFPv3 or ASIMD implies ARMv7.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureASIMD)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
}
// `AT_HWCAP2` provides ARMv8+ related flags.
static const LinuxHWCapMapping hwCap2Mapping[] = {
{ /* HWCAP2_AES */ (1 << 0), CpuInfo::kArmFeatureAES },
{ /* HWCAP2_PMULL */ (1 << 1), CpuInfo::kArmFeaturePMULL },
{ /* HWCAP2_SHA1 */ (1 << 2), CpuInfo::kArmFeatureSHA1 },
{ /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 },
{ /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP2, hwCap2Mapping, ASMJIT_ARRAY_SIZE(hwCap2Mapping));
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureAES ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureCRC32 ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeaturePMULL ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA1 ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA256)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV8);
}
#else
armPopulateBaselineA64Features(cpuInfo);
// `AT_HWCAP` provides ARMv8+ related flags.
static const LinuxHWCapMapping hwCapMapping[] = {
{ /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureASIMD },
{ /* HWCAP_AES */ (1 << 3), CpuInfo::kArmFeatureAES },
{ /* HWCAP_CRC32 */ (1 << 7), CpuInfo::kArmFeatureCRC32 },
{ /* HWCAP_PMULL */ (1 << 4), CpuInfo::kArmFeaturePMULL },
{ /* HWCAP_SHA1 */ (1 << 5), CpuInfo::kArmFeatureSHA1 },
{ /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 },
{ /* HWCAP_ATOMICS */ (1 << 8), CpuInfo::kArmFeatureAtomics64 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping));
// `AT_HWCAP2` is not used at the moment.
#endif
}
#endif // ASMJIT_OS_LINUX
ASMJIT_FAVOR_SIZE static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_OS_WINDOWS
armDetectCpuInfoOnWindows(cpuInfo);
#elif ASMJIT_OS_LINUX
armDetectCpuInfoOnLinux(cpuInfo);
#else
# error "[asmjit] armDetectCpuInfo() - Unsupported OS."
#endif
}
#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
// ============================================================================
// [asmjit::CpuInfo - Detect X86]
// ============================================================================
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
//! \internal
//!
//! X86 CPUID result.
struct CpuIdResult {
uint32_t eax, ebx, ecx, edx;
};
//! \internal
//!
//! Content of XCR register, result of XGETBV instruction.
struct XGetBVResult {
uint32_t eax, edx;
};
#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(15, 0, 30729) && ASMJIT_ARCH_X64
//! \internal
//!
//! HACK: VS2008 or less, 64-bit mode - `__cpuidex` doesn't exist! However,
//! 64-bit calling convention specifies the first parameter to be passed by
//! ECX, so we may be lucky if compiler doesn't move the register, otherwise
//! the result would be wrong.
static void ASMJIT_NOINLINE void x86CallCpuIdWorkaround(uint32_t inEcx, uint32_t inEax, CpuIdResult* result) noexcept {
__cpuid(reinterpret_cast<int*>(result), inEax);
}
#endif
//! \internal
//!
//! Wrapper to call `cpuid` instruction.
static void ASMJIT_INLINE x86CallCpuId(CpuIdResult* result, uint32_t inEax, uint32_t inEcx = 0) noexcept {
#if ASMJIT_CC_MSC && ASMJIT_CC_MSC_GE(15, 0, 30729)
__cpuidex(reinterpret_cast<int*>(result), inEax, inEcx);
#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X64
x86CallCpuIdWorkaround(inEcx, inEax, result);
#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X86
uint32_t paramEax = inEax;
uint32_t paramEcx = inEcx;
uint32_t* out = reinterpret_cast<uint32_t*>(result);
__asm {
mov eax, paramEax
mov ecx, paramEcx
mov edi, out
cpuid
mov dword ptr[edi + 0], eax
mov dword ptr[edi + 4], ebx
mov dword ptr[edi + 8], ecx
mov dword ptr[edi + 12], edx
}
#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X86
__asm__ __volatile__(
"mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a"(result->eax),
"=D"(result->ebx),
"=c"(result->ecx),
"=d"(result->edx)
: "a"(inEax),
"c"(inEcx));
#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG || ASMJIT_CC_INTEL) && ASMJIT_ARCH_X64
__asm__ __volatile__(
"mov %%rbx, %%rdi\n"
"cpuid\n"
"xchg %%rdi, %%rbx\n"
: "=a"(result->eax),
"=D"(result->ebx),
"=c"(result->ecx),
"=d"(result->edx)
: "a"(inEax),
"c"(inEcx));
#else
# error "[asmjit] x86CallCpuid() - Unsupported compiler."
#endif
}
//! \internal
//!
//! Wrapper to call `xgetbv` instruction.
static ASMJIT_INLINE void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept {
#if ASMJIT_CC_MSC_GE(16, 0, 40219) // 2010SP1+
uint64_t value = _xgetbv(inEcx);
result->eax = static_cast<uint32_t>(value & 0xFFFFFFFFU);
result->edx = static_cast<uint32_t>(value >> 32);
#elif ASMJIT_CC_GCC || ASMJIT_CC_CLANG
uint32_t outEax;
uint32_t outEdx;
// Replaced, because the world is not perfect:
// __asm__ __volatile__("xgetbv" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx));
__asm__ __volatile__(".byte 0x0F, 0x01, 0xd0" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx));
result->eax = outEax;
result->edx = outEdx;
#else
result->eax = 0;
result->edx = 0;
#endif
}
//! \internal
//!
//! Map a 12-byte vendor string returned by `cpuid` into a `CpuInfo::Vendor` ID.
static ASMJIT_INLINE uint32_t x86GetCpuVendorID(const char* vendorString) noexcept {
struct VendorData {
uint32_t id;
char text[12];
};
static const VendorData vendorList[] = {
{ CpuInfo::kVendorIntel , { 'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l' } },
{ CpuInfo::kVendorAMD , { 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D' } },
{ CpuInfo::kVendorVIA , { 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 } },
{ CpuInfo::kVendorVIA , { 'C', 'e', 'n', 't', 'a', 'u', 'r', 'H', 'a', 'u', 'l', 's' } }
};
uint32_t dw0 = reinterpret_cast<const uint32_t*>(vendorString)[0];
uint32_t dw1 = reinterpret_cast<const uint32_t*>(vendorString)[1];
uint32_t dw2 = reinterpret_cast<const uint32_t*>(vendorString)[2];
for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(vendorList); i++) {
if (dw0 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[0] &&
dw1 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[1] &&
dw2 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[2])
return vendorList[i].id;
}
return CpuInfo::kVendorNone;
}
static ASMJIT_INLINE void x86SimplifyBrandString(char* s) noexcept {
// Used to always clear the current character to ensure that the result
// doesn't contain garbage after the new zero terminator.
char* d = s;
char prev = 0;
char curr = s[0];
s[0] = '\0';
for (;;) {
if (curr == 0)
break;
if (curr == ' ') {
if (prev == '@' || s[1] == ' ' || s[1] == '@')
goto L_Skip;
}
d[0] = curr;
d++;
prev = curr;
L_Skip:
curr = *++s;
s[0] = '\0';
}
d[0] = '\0';
}
ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept {
uint32_t i, maxId;
CpuIdResult regs;
XGetBVResult xcr0 = { 0, 0 };
cpuInfo->_archInfo.init(ArchInfo::kTypeHost);
cpuInfo->addFeature(CpuInfo::kX86FeatureI486);
// --------------------------------------------------------------------------
// [CPUID EAX=0x0]
// --------------------------------------------------------------------------
// Get vendor string/id.
x86CallCpuId(&regs, 0x0);
maxId = regs.eax;
::memcpy(cpuInfo->_vendorString + 0, &regs.ebx, 4);
::memcpy(cpuInfo->_vendorString + 4, &regs.edx, 4);
::memcpy(cpuInfo->_vendorString + 8, &regs.ecx, 4);
cpuInfo->_vendorId = x86GetCpuVendorID(cpuInfo->_vendorString);
// --------------------------------------------------------------------------
// [CPUID EAX=0x1]
// --------------------------------------------------------------------------
if (maxId >= 0x1) {
// Get feature flags in ECX/EDX and family/model in EAX.
x86CallCpuId(&regs, 0x1);
// Fill family and model fields.
cpuInfo->_family = (regs.eax >> 8) & 0x0F;
cpuInfo->_model = (regs.eax >> 4) & 0x0F;
cpuInfo->_stepping = (regs.eax ) & 0x0F;
// Use extended family and model fields.
if (cpuInfo->_family == 0x0F) {
cpuInfo->_family += ((regs.eax >> 20) & 0xFF);
cpuInfo->_model += ((regs.eax >> 16) & 0x0F) << 4;
}
cpuInfo->_x86Data._processorType = ((regs.eax >> 12) & 0x03);
cpuInfo->_x86Data._brandIndex = ((regs.ebx ) & 0xFF);
cpuInfo->_x86Data._flushCacheLineSize = ((regs.ebx >> 8) & 0xFF) * 8;
cpuInfo->_x86Data._maxLogicalProcessors = ((regs.ebx >> 16) & 0xFF);
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE3);
if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCLMULQDQ);
if (regs.ecx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureMONITOR);
if (regs.ecx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSSE3);
if (regs.ecx & 0x00002000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG16B);
if (regs.ecx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_1);
if (regs.ecx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_2);
if (regs.ecx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMOVBE);
if (regs.ecx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePOPCNT);
if (regs.ecx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAESNI);
if (regs.ecx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE);
if (regs.ecx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureOSXSAVE);
if (regs.ecx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDRAND);
if (regs.edx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSC);
if (regs.edx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSR);
if (regs.edx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG8B);
if (regs.edx & 0x00008000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMOV);
if (regs.edx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH);
if (regs.edx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX);
if (regs.edx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR);
if (regs.edx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE)
.addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE)
.addFeature(CpuInfo::kX86FeatureSSE2);
if (regs.edx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMT);
// Get the content of XCR0 if supported by CPU and enabled by OS.
if ((regs.ecx & 0x0C000000U) == 0x0C000000U) {
x86CallXGetBV(&xcr0, 0);
}
// Detect AVX+.
if (regs.ecx & 0x10000000U) {
// - XCR0[2:1] == 11b
// XMM & YMM states need to be enabled by OS.
if ((xcr0.eax & 0x00000006U) == 0x00000006U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureAVX);
if (regs.ecx & 0x00001000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA);
if (regs.ecx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureF16C);
}
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0x7]
// --------------------------------------------------------------------------
// Detect new features if the processor supports CPUID-07.
bool maybeMPX = false;
if (maxId >= 0x7) {
x86CallCpuId(&regs, 0x7);
if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureFSGSBASE);
if (regs.ebx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI);
if (regs.ebx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureHLE);
if (regs.ebx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMEP);
if (regs.ebx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI2);
if (regs.ebx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureERMS);
if (regs.ebx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureRTM);
if (regs.ebx & 0x00004000U) maybeMPX = true;
if (regs.ebx & 0x00040000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDSEED);
if (regs.ebx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureADX);
if (regs.ebx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMAP);
if (regs.ebx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCOMMIT);
if (regs.ebx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSHOPT);
if (regs.ebx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLWB);
if (regs.ebx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSHA);
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHWT1);
// TSX is supported if at least one of `HLE` and `RTM` is supported.
if (regs.ebx & 0x00000810U) cpuInfo->addFeature(CpuInfo::kX86FeatureTSX);
// Detect AVX2.
if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) {
if (regs.ebx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX2);
}
// Detect AVX-512+.
if (regs.ebx & 0x00010000U) {
// - XCR0[2:1] == 11b
// XMM/YMM states need to be enabled by OS.
// - XCR0[7:5] == 111b
// Upper 256-bit of ZMM0-XMM15 and ZMM16-ZMM31 need to be enabled by the OS.
if ((xcr0.eax & 0x000000E6U) == 0x000000E6U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_F);
if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_DQ);
if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_IFMA);
if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_PFI);
if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_ERI);
if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_CDI);
if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_BW);
if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VL);
if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VBMI);
if (regs.ecx & 0x00004000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VPOPCNTDQ);
if (regs.edx & 0x00000004U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4VNNIW);
if (regs.edx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4FMAPS);
}
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0xD]
// --------------------------------------------------------------------------
if (maxId >= 0xD) {
x86CallCpuId(&regs, 0xD, 0);
// Both CPUID result and XCR0 has to be enabled to have support for MPX.
if (((regs.eax & xcr0.eax) & 0x00000018U) == 0x00000018U && maybeMPX)
cpuInfo->addFeature(CpuInfo::kX86FeatureMPX);
x86CallCpuId(&regs, 0xD, 1);
if (regs.eax & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEOPT);
if (regs.eax & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEC);
if (regs.eax & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVES);
}
// --------------------------------------------------------------------------
// [CPUID EAX=0x80000000...maxId]
// --------------------------------------------------------------------------
// The highest EAX that we understand.
uint32_t kHighestProcessedEAX = 0x80000008U;
// Several CPUID calls are required to get the whole branc string. It's easy
// to copy one DWORD at a time instead of performing a byte copy.
uint32_t* brand = reinterpret_cast<uint32_t*>(cpuInfo->_brandString);
i = maxId = 0x80000000U;
do {
x86CallCpuId(&regs, i);
switch (i) {
case 0x80000000U:
maxId = std::min<uint32_t>(regs.eax, kHighestProcessedEAX);
break;
case 0x80000001U:
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureLAHFSAHF);
if (regs.ecx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureLZCNT);
if (regs.ecx & 0x00000040U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4A);
if (regs.ecx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSSE);
if (regs.ecx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHW);
if (regs.ecx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureTBM);
if (regs.edx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureNX);
if (regs.edx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSROPT);
if (regs.edx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSCP);
if (regs.edx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW2)
.addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW);
if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) {
if (regs.ecx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureXOP);
if (regs.ecx & 0x00010000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA4);
}
// These seem to be only supported by AMD.
if (cpuInfo->getVendorId() == CpuInfo::kVendorAMD) {
if (regs.ecx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureALTMOVCR8);
}
break;
case 0x80000002U:
case 0x80000003U:
case 0x80000004U:
*brand++ = regs.eax;
*brand++ = regs.ebx;
*brand++ = regs.ecx;
*brand++ = regs.edx;
// Go directly to the last one.
if (i == 0x80000004U) i = 0x80000008U - 1;
break;
case 0x80000008U:
if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLZERO);
break;
}
} while (++i <= maxId);
// Simplify CPU brand string by removing unnecessary spaces.
x86SimplifyBrandString(cpuInfo->_brandString);
}
#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
// ============================================================================
// [asmjit::CpuInfo - Detect - HWThreadsCount]
// ============================================================================
static ASMJIT_INLINE uint32_t cpuDetectHWThreadsCount() noexcept {
#if ASMJIT_OS_WINDOWS
SYSTEM_INFO info;
::GetSystemInfo(&info);
return info.dwNumberOfProcessors;
#elif defined(ASMJIT_OS_POSIX) && defined(_SC_NPROCESSORS_ONLN)
// It seems that sysconf returns the number of "logical" processors on both
// mac and linux. So we get the number of "online logical" processors.
#elif ASMJIT_OS_POSIX && defined(_SC_NPROCESSORS_ONLN)
long res = ::sysconf(_SC_NPROCESSORS_ONLN);
if (res == -1) return 1;
if (res <= 0) return 1;
return static_cast<uint32_t>(res);
#else
return 1;
#endif
}
// ============================================================================
// [asmjit::CpuInfo - Detect]
// ============================================================================
ASMJIT_FAVOR_SIZE void CpuInfo::detect() noexcept {
reset();
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
armDetectCpuInfo(this);
#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
x86DetectCpuInfo(this);
#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
_hwThreadsCount = cpuDetectHWThreadsCount();
}
// ============================================================================
// [asmjit::CpuInfo - GetHost]
// ============================================================================
#if defined(ASMJIT_ARCH_X86) || defined(ASMJIT_ARCH_X64)
struct AutoX86CpuInfo : public X86CpuInfo {
ASMJIT_INLINE AutoX86CpuInfo() : X86CpuInfo() {
X86CpuUtil::detect(this);
}
struct HostCpuInfo : public CpuInfo {
ASMJIT_INLINE HostCpuInfo() noexcept : CpuInfo() { detect(); }
};
#else
#error "AsmJit - Unsupported CPU."
#endif
const CpuInfo* CpuInfo::getHost() {
#if defined(ASMJIT_ARCH_X86) || defined(ASMJIT_ARCH_X64)
static AutoX86CpuInfo cpuInfo;
#else
#error "AsmJit - Unsupported CPU."
#endif
return &cpuInfo;
const CpuInfo& CpuInfo::getHost() noexcept {
static HostCpuInfo host;
return host;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
......@@ -8,132 +8,358 @@
#ifndef _ASMJIT_BASE_CPUINFO_H
#define _ASMJIT_BASE_CPUINFO_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Dependencies]
#include "../base/arch.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::CpuVendor]
// [asmjit::CpuFeatures]
// ============================================================================
//! Cpu vendor ID.
//!
//! Vendor IDs are specific to AsmJit library. During the library initialization
//! AsmJit checks host CPU and tries to identify the vendor based on the CPUID
//! calls. Some manufacturers changed their vendor strings and AsmJit is aware
//! of that - it checks multiple combinations and decides which vendor ID should
//! be used.
ASMJIT_ENUM(CpuVendor) {
//! No/Unknown vendor.
kCpuVendorNone = 0,
//! Intel vendor.
kCpuVendorIntel = 1,
//! AMD vendor.
kCpuVendorAmd = 2,
//! VIA vendor.
kCpuVendorVia = 3
class CpuFeatures {
public:
typedef uintptr_t BitWord;
enum {
kMaxFeatures = 128,
kBitWordSize = static_cast<int>(sizeof(BitWord)) * 8,
kNumBitWords = kMaxFeatures / kBitWordSize
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CpuFeatures() noexcept { reset(); }
ASMJIT_INLINE CpuFeatures(const CpuFeatures& other) noexcept = default;
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void init(const CpuFeatures& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Get all features as `BitWord` array.
ASMJIT_INLINE BitWord* getBits() noexcept { return _bits; }
//! Get all features as `BitWord` array (const).
ASMJIT_INLINE const BitWord* getBits() const noexcept { return _bits; }
//! Get if feature `feature` is present.
ASMJIT_INLINE bool has(uint32_t feature) const noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
return static_cast<bool>((_bits[idx] >> bit) & 0x1);
}
//! Get if all features as defined by `other` are present.
ASMJIT_INLINE bool hasAll(const CpuFeatures& other) const noexcept {
for (uint32_t i = 0; i < kNumBitWords; i++)
if ((_bits[i] & other._bits[i]) != other._bits[i])
return false;
return true;
}
//! Add a CPU `feature`.
ASMJIT_INLINE CpuFeatures& add(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
_bits[idx] |= static_cast<BitWord>(1) << bit;
return *this;
}
//! Remove a CPU `feature`.
ASMJIT_INLINE CpuFeatures& remove(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
_bits[idx] &= ~(static_cast<BitWord>(1) << bit);
return *this;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
BitWord _bits[kNumBitWords];
};
// ============================================================================
// [asmjit::CpuInfo]
// ============================================================================
//! Base cpu information.
struct CpuInfo {
ASMJIT_NO_COPY(CpuInfo)
//! CPU information.
class CpuInfo {
public:
//! CPU vendor ID.
ASMJIT_ENUM(Vendor) {
kVendorNone = 0, //!< Generic or unknown.
kVendorIntel = 1, //!< Intel vendor.
kVendorAMD = 2, //!< AMD vendor.
kVendorVIA = 3 //!< VIA vendor.
};
//! \internal
enum {
kFeaturesPerUInt32 = static_cast<int>(sizeof(uint32_t)) * 8
//! ARM/ARM64 CPU features.
ASMJIT_ENUM(ArmFeatures) {
kArmFeatureV6 = 1, //!< ARMv6 instruction set.
kArmFeatureV7, //!< ARMv7 instruction set.
kArmFeatureV8, //!< ARMv8 instruction set.
kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (THUMB mode).
kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (THUMB mode).
kArmFeatureVFPv2, //!< CPU provides VFPv2 instruction set.
kArmFeatureVFPv3, //!< CPU provides VFPv3 instruction set.
kArmFeatureVFPv4, //!< CPU provides VFPv4 instruction set.
kArmFeatureVFP_D32, //!< CPU provides 32 VFP-D (64-bit) registers.
kArmFeatureEDSP, //!< CPU provides EDSP extensions.
kArmFeatureASIMD, //!< CPU provides 'Advanced SIMD'.
kArmFeatureIDIVA, //!< CPU provides hardware SDIV and UDIV (ARM mode).
kArmFeatureIDIVT, //!< CPU provides hardware SDIV and UDIV (THUMB mode).
kArmFeatureAES, //!< CPU provides AES instructions (ARM64 only).
kArmFeatureCRC32, //!< CPU provides CRC32 instructions.
kArmFeaturePMULL, //!< CPU provides PMULL instructions (ARM64 only).
kArmFeatureSHA1, //!< CPU provides SHA1 instructions.
kArmFeatureSHA256, //!< CPU provides SHA256 instructions.
kArmFeatureAtomics64, //!< CPU provides 64-bit load/store atomics (ARM64 only).
kArmFeaturesCount //!< Count of ARM/ARM64 CPU features.
};
//! X86/X64 CPU features.
ASMJIT_ENUM(X86Features) {
kX86FeatureI486 = 1, //!< CPU is at least I486.
kX86FeatureNX, //!< CPU has Not-Execute-Bit.
kX86FeatureMT, //!< CPU has multi-threading.
kX86FeatureALTMOVCR8, //!< CPU supports `LOCK MOV CR8` (AMD CPUs).
kX86FeatureCMOV, //!< CPU has CMOV.
kX86FeatureCMPXCHG8B, //!< CPU has CMPXCHG8B.
kX86FeatureCMPXCHG16B, //!< CPU has CMPXCHG16B (x64).
kX86FeatureMSR, //!< CPU has RDMSR/WRMSR.
kX86FeatureRDTSC, //!< CPU has RDTSC.
kX86FeatureRDTSCP, //!< CPU has RDTSCP.
kX86FeatureCLFLUSH, //!< CPU has CLFUSH.
kX86FeatureCLFLUSHOPT, //!< CPU has CLFUSHOPT.
kX86FeatureCLWB, //!< CPU has CLWB.
kX86FeatureCLZERO, //!< CPU has CLZERO.
kX86FeaturePCOMMIT, //!< CPU has PCOMMIT.
kX86FeaturePREFETCHW, //!< CPU has PREFETCHW.
kX86FeaturePREFETCHWT1, //!< CPU has PREFETCHWT1.
kX86FeatureLAHFSAHF, //!< CPU has LAHF/SAHF.
kX86FeatureFXSR, //!< CPU has FXSAVE/FXRSTOR.
kX86FeatureFXSROPT, //!< CPU has FXSAVE/FXRSTOR (optimized).
kX86FeatureMMX, //!< CPU has MMX.
kX86FeatureMMX2, //!< CPU has extended MMX.
kX86Feature3DNOW, //!< CPU has 3DNOW.
kX86Feature3DNOW2, //!< CPU has 3DNOW2 (enhanced).
kX86FeatureGEODE, //!< CPU has GEODE extensions (few additions to 3DNOW).
kX86FeatureSSE, //!< CPU has SSE.
kX86FeatureSSE2, //!< CPU has SSE2.
kX86FeatureSSE3, //!< CPU has SSE3.
kX86FeatureSSSE3, //!< CPU has SSSE3.
kX86FeatureSSE4A, //!< CPU has SSE4.A.
kX86FeatureSSE4_1, //!< CPU has SSE4.1.
kX86FeatureSSE4_2, //!< CPU has SSE4.2.
kX86FeatureMSSE, //!< CPU has Misaligned SSE (MSSE).
kX86FeatureMONITOR, //!< CPU has MONITOR and MWAIT.
kX86FeatureMOVBE, //!< CPU has MOVBE.
kX86FeaturePOPCNT, //!< CPU has POPCNT.
kX86FeatureLZCNT, //!< CPU has LZCNT.
kX86FeatureAESNI, //!< CPU has AESNI.
kX86FeaturePCLMULQDQ, //!< CPU has PCLMULQDQ.
kX86FeatureRDRAND, //!< CPU has RDRAND.
kX86FeatureRDSEED, //!< CPU has RDSEED.
kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention).
kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention).
kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256.
kX86FeatureXSAVE, //!< CPU has XSAVE support (XSAVE/XRSTOR, XSETBV/XGETBV, and XCR).
kX86FeatureXSAVEC, //!< CPU has XSAVEC support (XSAVEC).
kX86FeatureXSAVES, //!< CPU has XSAVES support (XSAVES/XRSTORS).
kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support (XSAVEOPT/XSAVEOPT64).
kX86FeatureOSXSAVE, //!< CPU has XSAVE enabled by OS.
kX86FeatureAVX, //!< CPU has AVX.
kX86FeatureAVX2, //!< CPU has AVX2.
kX86FeatureF16C, //!< CPU has F16C.
kX86FeatureFMA, //!< CPU has FMA.
kX86FeatureFMA4, //!< CPU has FMA4.
kX86FeatureXOP, //!< CPU has XOP.
kX86FeatureBMI, //!< CPU has BMI (bit manipulation instructions #1).
kX86FeatureBMI2, //!< CPU has BMI2 (bit manipulation instructions #2).
kX86FeatureADX, //!< CPU has ADX (multi-precision add-carry instruction extensions).
kX86FeatureTBM, //!< CPU has TBM (trailing bit manipulation).
kX86FeatureMPX, //!< CPU has MPX (memory protection extensions).
kX86FeatureHLE, //!< CPU has HLE.
kX86FeatureRTM, //!< CPU has RTM.
kX86FeatureTSX, //!< CPU has TSX.
kX86FeatureERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB).
kX86FeatureFSGSBASE, //!< CPU has FSGSBASE.
kX86FeatureAVX512_F, //!< CPU has AVX512-F (foundation).
kX86FeatureAVX512_CDI, //!< CPU has AVX512-CDI (conflict detection).
kX86FeatureAVX512_PFI, //!< CPU has AVX512-PFI (prefetch instructions).
kX86FeatureAVX512_ERI, //!< CPU has AVX512-ERI (exponential and reciprocal).
kX86FeatureAVX512_DQ, //!< CPU has AVX512-DQ (DWORD/QWORD).
kX86FeatureAVX512_BW, //!< CPU has AVX512-BW (BYTE/WORD).
kX86FeatureAVX512_VL, //!< CPU has AVX512-VL (vector length extensions).
kX86FeatureAVX512_IFMA, //!< CPU has AVX512-IFMA (integer fused-multiply-add using 52-bit precision).
kX86FeatureAVX512_VBMI, //!< CPU has AVX512-VBMI (vector byte manipulation).
kX86FeatureAVX512_VPOPCNTDQ, //!< CPU has AVX512-VPOPCNTDQ (VPOPCNT[D|Q] instructions).
kX86FeatureAVX512_4VNNIW, //!< CPU has AVX512-VNNIW (vector NN instructions word variable precision).
kX86FeatureAVX512_4FMAPS, //!< CPU has AVX512-FMAPS (FMA packed single).
kX86FeaturesCount //!< Count of X86/X64 CPU features.
};
// --------------------------------------------------------------------------
// [ArmInfo]
// --------------------------------------------------------------------------
struct ArmData {
};
// --------------------------------------------------------------------------
// [X86Info]
// --------------------------------------------------------------------------
struct X86Data {
uint32_t _processorType; //!< Processor type.
uint32_t _brandIndex; //!< Brand index.
uint32_t _flushCacheLineSize; //!< Flush cache line size (in bytes).
uint32_t _maxLogicalProcessors; //!< Maximum number of addressable IDs for logical processors.
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CpuInfo(uint32_t size) : _size(size) {}
ASMJIT_INLINE CpuInfo() noexcept { reset(); }
ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept = default;
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize CpuInfo to the given architecture, see \ArchInfo.
ASMJIT_INLINE void initArch(uint32_t archType, uint32_t archMode = 0) noexcept {
_archInfo.init(archType, archMode);
}
ASMJIT_INLINE void init(const CpuInfo& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Detect]
// --------------------------------------------------------------------------
ASMJIT_API void detect() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get CPU vendor string.
ASMJIT_INLINE const char* getVendorString() const { return _vendorString; }
//! Get CPU brand string.
ASMJIT_INLINE const char* getBrandString() const { return _brandString; }
//! Get generic architecture information.
ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; }
//! Get CPU architecture type, see \ArchInfo::Type.
ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); }
//! Get CPU architecture sub-type, see \ArchInfo::SubType.
ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); }
//! Get CPU vendor ID.
ASMJIT_INLINE uint32_t getVendorId() const { return _vendorId; }
//! Get CPU vendor ID.
ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; }
//! Get CPU family ID.
ASMJIT_INLINE uint32_t getFamily() const { return _family; }
ASMJIT_INLINE uint32_t getFamily() const noexcept { return _family; }
//! Get CPU model ID.
ASMJIT_INLINE uint32_t getModel() const { return _model; }
ASMJIT_INLINE uint32_t getModel() const noexcept { return _model; }
//! Get CPU stepping.
ASMJIT_INLINE uint32_t getStepping() const { return _stepping; }
ASMJIT_INLINE uint32_t getStepping() const noexcept { return _stepping; }
//! Get number of hardware threads available.
ASMJIT_INLINE uint32_t getHwThreadsCount() const { return _hwThreadsCount; }
ASMJIT_INLINE uint32_t getHwThreadsCount() const noexcept {
return _hwThreadsCount;
}
//! Get all CPU features.
ASMJIT_INLINE const CpuFeatures& getFeatures() const noexcept { return _features; }
//! Get whether CPU has a `feature`.
ASMJIT_INLINE bool hasFeature(uint32_t feature) const {
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { return _features.has(feature); }
//! Add a CPU `feature`.
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { _features.add(feature); return *this; }
//! Get CPU vendor string.
ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; }
//! Get CPU brand string.
ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; }
return static_cast<bool>(
(_features[feature / kFeaturesPerUInt32] >> (feature % kFeaturesPerUInt32)) & 0x1);
// --------------------------------------------------------------------------
// [Accessors - ARM]
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// [Accessors - X86]
// --------------------------------------------------------------------------
//! Get processor type.
ASMJIT_INLINE uint32_t getX86ProcessorType() const noexcept {
return _x86Data._processorType;
}
//! Add a CPU `feature`.
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) {
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
//! Get brand index.
ASMJIT_INLINE uint32_t getX86BrandIndex() const noexcept {
return _x86Data._brandIndex;
}
_features[feature / kFeaturesPerUInt32] |= (1U << (feature % kFeaturesPerUInt32));
return *this;
//! Get flush cache line size.
ASMJIT_INLINE uint32_t getX86FlushCacheLineSize() const noexcept {
return _x86Data._flushCacheLineSize;
}
//! Get maximum logical processors count.
ASMJIT_INLINE uint32_t getX86MaxLogicalProcessors() const noexcept {
return _x86Data._maxLogicalProcessors;
}
// --------------------------------------------------------------------------
// [Statics]
// --------------------------------------------------------------------------
//! Detect the number of hardware threads.
static ASMJIT_API uint32_t detectHwThreadsCount();
//! Get host cpu.
static ASMJIT_API const CpuInfo* getHost();
//! Get the host CPU information.
ASMJIT_API static const CpuInfo& getHost() noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Size of the structure in bytes.
uint32_t _size;
//! Cpu short vendor string.
char _vendorString[16];
//! Cpu long vendor string (brand).
char _brandString[64];
//! Cpu vendor id, see \ref CpuVendor.
uint32_t _vendorId;
//! Cpu family ID.
uint32_t _family;
//! Cpu model ID.
uint32_t _model;
//! Cpu stepping.
uint32_t _stepping;
//! Number of hardware threads.
uint32_t _hwThreadsCount;
//! Cpu features bitfield.
uint32_t _features[4];
ArchInfo _archInfo; //!< CPU architecture information.
uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor.
uint32_t _family; //!< CPU family ID.
uint32_t _model; //!< CPU model ID.
uint32_t _stepping; //!< CPU stepping.
uint32_t _hwThreadsCount; //!< Number of hardware threads.
CpuFeatures _features; //!< CPU features.
char _vendorString[16]; //!< CPU vendor string.
char _brandString[64]; //!< CPU brand string.
// Architecture specific data.
union {
ArmData _armData;
X86Data _x86Data;
};
};
//! \}
......@@ -141,7 +367,7 @@ struct CpuInfo {
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CPUINFO_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/cputicks.h"
// [Dependencies - Posix]
#if defined(ASMJIT_OS_POSIX)
# include <time.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
// [Dependencies - Mac]
#if defined(ASMJIT_OS_MAC)
# include <mach/mach_time.h>
#endif // ASMJIT_OS_MAC
// [Dependencies - Windows]
#if defined(ASMJIT_OS_WINDOWS)
// `_InterlockedCompareExchange` is only available as intrinsic (MS Compiler).
# if defined(_MSC_VER) && _MSC_VER >= 1400
# include <intrin.h>
# pragma intrinsic(_InterlockedCompareExchange)
# else
# define _InterlockedCompareExchange InterlockedCompareExchange
# endif // _MSC_VER
#endif // ASMJIT_OS_WINDOWS
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CpuTicks - Windows]
// ============================================================================
#if defined(ASMJIT_OS_WINDOWS)
static volatile uint32_t CpuTicks_hiResOk;
static volatile double CpuTicks_hiResFreq;
uint32_t CpuTicks::now() {
do {
uint32_t hiResOk = CpuTicks_hiResOk;
if (hiResOk == 1) {
LARGE_INTEGER now;
if (!::QueryPerformanceCounter(&now))
break;
return (int64_t)(double(now.QuadPart) / CpuTicks_hiResFreq);
}
if (hiResOk == 0) {
LARGE_INTEGER qpf;
if (!::QueryPerformanceFrequency(&qpf)) {
_InterlockedCompareExchange((LONG*)&CpuTicks_hiResOk, 0xFFFFFFFF, 0);
break;
}
LARGE_INTEGER now;
if (!::QueryPerformanceCounter(&now)) {
_InterlockedCompareExchange((LONG*)&CpuTicks_hiResOk, 0xFFFFFFFF, 0);
break;
}
double freqDouble = double(qpf.QuadPart) / 1000.0;
CpuTicks_hiResFreq = freqDouble;
_InterlockedCompareExchange((LONG*)&CpuTicks_hiResOk, 1, 0);
return static_cast<uint32_t>(
static_cast<int64_t>(double(now.QuadPart) / freqDouble) & 0xFFFFFFFF);
}
} while (0);
// Bail to a less precise GetTickCount().
return ::GetTickCount();
}
// ============================================================================
// [asmjit::CpuTicks - Mac]
// ============================================================================
#elif defined(ASMJIT_OS_MAC)
static mach_timebase_info_data_t CpuTicks_machTime;
uint32_t CpuTicks::now() {
// Initialize the first time CpuTicks::now() is called (See Apple's QA1398).
if (CpuTicks_machTime.denom == 0) {
if (mach_timebase_info(&CpuTicks_machTime) != KERN_SUCCESS);
return 0;
}
// mach_absolute_time() returns nanoseconds, we need just milliseconds.
uint64_t t = mach_absolute_time() / 1000000;
t = t * CpuTicks_machTime.numer / CpuTicks_machTime.denom;
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
}
// ============================================================================
// [asmjit::CpuTicks - Posix]
// ============================================================================
#else
uint32_t CpuTicks::now() {
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
return 0;
uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000);
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
#else // _POSIX_MONOTONIC_CLOCK
#error "AsmJit - Unsupported OS."
return 0;
#endif // _POSIX_MONOTONIC_CLOCK
}
#endif // ASMJIT_OS
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CPUTICKS_H
#define _ASMJIT_BASE_CPUTICKS_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::CpuTicks]
// ============================================================================
//! CPU ticks utilities.
struct CpuTicks {
//! Get the current CPU ticks for benchmarking (1ms resolution).
static ASMJIT_API uint32_t now();
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CPUTICKS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_ERROR_H
#define _ASMJIT_BASE_ERROR_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::ErrorCode]
// ============================================================================
//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Heap memory allocation failed.
kErrorNoHeapMemory = 1,
//! Virtual memory allocation failed.
kErrorNoVirtualMemory = 2,
//! Invalid argument.
kErrorInvalidArgument = 3,
//! Invalid state.
kErrorInvalidState = 4,
//! No code generated.
//!
//! Returned by runtime if the code-generator contains no code.
kErrorNoCodeGenerated = 5,
//! Code generated is too large to fit in memory reserved.
//!
//! Returned by `StaticRuntime` in case that the code generated is too large
//! to fit in the memory already reserved for it.
kErrorCodeTooLarge = 6,
//! Label is already bound.
kErrorLabelAlreadyBound = 7,
//! Unknown instruction (an instruction ID is out of bounds or instruction
//! name is invalid).
kErrorUnknownInst = 8,
//! Illegal instruction.
//!
//! This status code can also be returned in X64 mode if AH, BH, CH or DH
//! registers have been used together with a REX prefix. The instruction
//! is not encodable in such case.
//!
//! Example of raising `kErrorIllegalInst` error.
//!
//! ~~~
//! // Invalid address size.
//! a.mov(dword_ptr(eax), al);
//!
//! // Undecodable instruction - AH used with R10, however R10 can only be
//! // encoded by using REX prefix, which conflicts with AH.
//! a.mov(byte_ptr(r10), ah);
//! ~~~
//!
//! \note In debug mode assertion is raised instead of returning an error.
kErrorIllegalInst = 9,
//! Illegal (unencodable) addressing used.
kErrorIllegalAddresing = 10,
//! Illegal (unencodable) displacement used.
//!
//! X86/X64
//! -------
//!
//! Short form of jump instruction has been used, but the displacement is out
//! of bounds.
kErrorIllegalDisplacement = 11,
//! A variable has been assigned more than once to a function argument (Compiler).
kErrorOverlappedArgs = 12,
//! Count of AsmJit error codes.
kErrorCount = 13
};
// ============================================================================
// [asmjit::Error]
// ============================================================================
//! AsmJit error type (unsigned integer).
typedef uint32_t Error;
// ============================================================================
// [asmjit::ErrorHandler]
// ============================================================================
//! Error handler.
//!
//! Error handler can be used to override the default behavior of `CodeGen`
//! error handling and propagation. See `handleError` on how to override it.
//!
//! Please note that `addRef` and `release` functions are used, but there is
//! no reference counting implemented by default, reimplement to change the
//! default behavior.
struct ASMJIT_VCLASS ErrorHandler {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `ErrorHandler` instance.
ASMJIT_API ErrorHandler();
//! Destroy the `ErrorHandler` instance.
ASMJIT_API virtual ~ErrorHandler();
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Reference this error handler.
//!
//! \note This member function is provided for convenience. The default
//! implementation does nothing. If you are working in environment where
//! multiple `ErrorHandler` instances are used by a different code generators
//! you may provide your own functionality for reference counting. In that
//! case `addRef()` and `release()` functions should be overridden.
ASMJIT_API virtual ErrorHandler* addRef() const;
//! Release this error handler.
//!
//! \note This member function is provided for convenience. See `addRef()`
//! for more detailed information related to reference counting.
ASMJIT_API virtual void release();
//! Error handler (pure).
//!
//! Error handler is called when an error happened. An error can happen in
//! many places, but error handler is mostly used by `Assembler` and
//! `Compiler` classes to report anything that may cause incorrect code
//! generation. There are multiple ways how the error handler can be used
//! and each has it's pros/cons.
//!
//! AsmJit library doesn't use exceptions and can be compiled with or without
//! exception handling support. Even if the AsmJit library is compiled without
//! exceptions it is exception-safe and handleError() can report an incoming
//! error by throwing an exception of any type. It's guaranteed that the
//! exception won't be catched by AsmJit and will be propagated to the code
//! calling AsmJit `Assembler` or `Compiler` methods. Alternative to
//! throwing an exception is using `setjmp()` and `longjmp()` pair available
//! in the standard C library.
//!
//! If the exception or setjmp() / longjmp() mechanism is used, the state of
//! the `BaseAssember` or `Compiler` is unchanged and if it's possible the
//! execution (instruction serialization) can continue. However if the error
//! happened during any phase that translates or modifies the stored code
//! (for example relocation done by `Assembler` or analysis/translation
//! done by `Compiler`) the execution can't continue and the error will
//! be also stored in `Assembler` or `Compiler`.
//!
//! Finally, if no exceptions nor setjmp() / longjmp() mechanisms were used,
//! you can still implement a compatible handling by returning from your
//! error handler. Returning `true` means that error was reported and AsmJit
//! should continue execution, but `false` sets the rror immediately to the
//! `Assembler` or `Compiler` and execution shouldn't continue (this
//! is the default behavior in case no error handler is used).
virtual bool handleError(Error code, const char* message) = 0;
};
// ============================================================================
// [asmjit::ErrorUtil]
// ============================================================================
//! Error utilities.
struct ErrorUtil {
#if !defined(ASMJIT_DISABLE_NAMES)
//! Get a printable version of AsmJit `Error` code.
static ASMJIT_API const char* asString(Error code);
#endif // ASMJIT_DISABLE_NAMES
};
//! \}
// ============================================================================
// [ASMJIT_PROPAGATE_ERROR]
// ============================================================================
//! \internal
//!
//! Used by AsmJit to return the `_Exp_` result if it's an error.
#define ASMJIT_PROPAGATE_ERROR(_Exp_) \
do { \
::asmjit::Error errval_ = (_Exp_); \
if (errval_ != ::asmjit::kErrorOk) \
return errval_; \
} while (0)
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_ERROR_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/arch.h"
#include "../base/func.h"
#if defined(ASMJIT_BUILD_X86)
#include "../x86/x86internal_p.h"
#include "../x86/x86operand.h"
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
#include "../arm/arminternal_p.h"
#include "../arm/armoperand.h"
#endif // ASMJIT_BUILD_ARM
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CallConv - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept {
reset();
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::initCallConv(*this, ccId);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::initCallConv(*this, ccId);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncDetail - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) {
uint32_t ccId = sign.getCallConv();
CallConv& cc = _callConv;
uint32_t argCount = sign.getArgCount();
if (ASMJIT_UNLIKELY(argCount > kFuncArgCount))
return DebugUtils::errored(kErrorInvalidArgument);
ASMJIT_PROPAGATE(cc.init(ccId));
uint32_t gpSize = (cc.getArchType() == ArchInfo::kTypeX86) ? 4 : 8;
uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize);
const uint8_t* args = sign.getArgs();
for (uint32_t i = 0; i < argCount; i++) {
Value& arg = _args[i];
arg.initTypeId(TypeId::deabstract(args[i], deabstractDelta));
}
_argCount = static_cast<uint8_t>(argCount);
uint32_t ret = sign.getRet();
if (ret != TypeId::kVoid) {
_rets[0].initTypeId(TypeId::deabstract(ret, deabstractDelta));
_retCount = 1;
}
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::initFuncDetail(*this, sign, gpSize);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::initFuncDetail(*this, sign, gpSize);
#endif // ASMJIT_BUILD_ARM
// We should never bubble here as if `cc.init()` succeeded then there has to
// be an implementation for the current architecture. However, stay safe.
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncFrameLayout - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncFrameLayout::init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept {
uint32_t ccId = func.getCallConv().getId();
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::initFrameLayout(*this, func, ffi);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::initFrameLayout(*this, func, ffi);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncArgsMapper]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncArgsMapper::updateFrameInfo(FuncFrameInfo& ffi) const noexcept {
const FuncDetail* func = getFuncDetail();
if (!func) return DebugUtils::errored(kErrorInvalidState);
uint32_t ccId = func->getCallConv().getId();
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::argsToFrameInfo(*this, ffi);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::argsToFrameInfo(*this, ffi);
#endif // ASMJIT_BUILD_X86
return DebugUtils::errored(kErrorInvalidArch);
}
// ============================================================================
// [asmjit::FuncUtils]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncUtils::emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout) {
#if defined(ASMJIT_BUILD_X86)
if (emitter->getArchInfo().isX86Family())
return X86Internal::emitProlog(static_cast<X86Emitter*>(emitter), layout);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (emitter->getArchInfo().isArmFamily())
return ArmInternal::emitProlog(static_cast<ArmEmitter*>(emitter), layout);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArch);
}
ASMJIT_FAVOR_SIZE Error FuncUtils::emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout) {
#if defined(ASMJIT_BUILD_X86)
if (emitter->getArchInfo().isX86Family())
return X86Internal::emitEpilog(static_cast<X86Emitter*>(emitter), layout);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (emitter->getArchInfo().isArmFamily())
return ArmInternal::emitEpilog(static_cast<ArmEmitter*>(emitter), layout);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArch);
}
ASMJIT_FAVOR_SIZE Error FuncUtils::allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args) {
#if defined(ASMJIT_BUILD_X86)
if (emitter->getArchInfo().isX86Family())
return X86Internal::allocArgs(static_cast<X86Emitter*>(emitter), layout, args);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (emitter->getArchInfo().isArmFamily())
return ArmInternal::allocArgs(static_cast<ArmEmitter*>(emitter), layout, args);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArch);
}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_FUNC_H
#define _ASMJIT_BASE_FUNC_H
#include "../asmjit_build.h"
// [Dependencies]
#include "../base/arch.h"
#include "../base/operand.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [Forward Declarations]
// ============================================================================
class CodeEmitter;
// ============================================================================
// [asmjit::CallConv]
// ============================================================================
//! Function calling convention.
//!
//! Function calling convention is a scheme that defines how function parameters
//! are passed and how function returns its result. AsmJit defines a variety of
//! architecture and OS specific calling conventions and also provides a compile
//! time detection to make JIT code-generation easier.
struct CallConv {
//! Calling convention id.
ASMJIT_ENUM(Id) {
//! None or invalid (can't be used).
kIdNone = 0,
// ------------------------------------------------------------------------
// [Universal]
// ------------------------------------------------------------------------
// TODO: To make this possible we need to know target ARCH and ABI.
/*
// Universal calling conventions are applicable to any target and are
// converted to target dependent conventions at runtime. The purpose of
// these conventions is to make using functions less target dependent.
kIdCDecl = 1,
kIdStdCall = 2,
kIdFastCall = 3,
//! AsmJit specific calling convention designed for calling functions
//! inside a multimedia code like that don't use many registers internally,
//! but are long enough to be called and not inlined. These functions are
//! usually used to calculate trigonometric functions, logarithms, etc...
kIdFastEval2 = 10,
kIdFastEval3 = 11,
kIdFastEval4 = 12,
*/
// ------------------------------------------------------------------------
// [X86]
// ------------------------------------------------------------------------
//! X86 `__cdecl` calling convention (used by C runtime and libraries).
kIdX86CDecl = 16,
//! X86 `__stdcall` calling convention (used mostly by WinAPI).
kIdX86StdCall = 17,
//! X86 `__thiscall` calling convention (MSVC/Intel).
kIdX86MsThisCall = 18,
//! X86 `__fastcall` convention (MSVC/Intel).
kIdX86MsFastCall = 19,
//! X86 `__fastcall` convention (GCC and Clang).
kIdX86GccFastCall = 20,
//! X86 `regparm(1)` convention (GCC and Clang).
kIdX86GccRegParm1 = 21,
//! X86 `regparm(2)` convention (GCC and Clang).
kIdX86GccRegParm2 = 22,
//! X86 `regparm(3)` convention (GCC and Clang).
kIdX86GccRegParm3 = 23,
kIdX86FastEval2 = 29,
kIdX86FastEval3 = 30,
kIdX86FastEval4 = 31,
//! X64 calling convention defined by WIN64-ABI.
//!
//! Links:
//! * <http://msdn.microsoft.com/en-us/library/9b372w95.aspx>.
kIdX86Win64 = 32,
//! X64 calling convention used by Unix platforms (SYSV/AMD64-ABI).
kIdX86SysV64 = 33,
kIdX64FastEval2 = 45,
kIdX64FastEval3 = 46,
kIdX64FastEval4 = 47,
// ------------------------------------------------------------------------
// [ARM]
// ------------------------------------------------------------------------
//! Legacy calling convention, floating point arguments are passed via GP registers.
kIdArm32SoftFP = 48,
//! Modern calling convention, uses VFP registers to pass floating point arguments.
kIdArm32HardFP = 49,
// ------------------------------------------------------------------------
// [Internal]
// ------------------------------------------------------------------------
_kIdX86Start = 16, //!< \internal
_kIdX86End = 31, //!< \internal
_kIdX64Start = 32, //!< \internal
_kIdX64End = 47, //!< \internal
_kIdArmStart = 48, //!< \internal
_kIdArmEnd = 49, //!< \internal
// ------------------------------------------------------------------------
// [Host]
// ------------------------------------------------------------------------
#if defined(ASMJIT_DOCGEN)
//! Default calling convention based on the current C++ compiler's settings.
//!
//! NOTE: This should be always the same as `kIdHostCDecl`, but some
//! compilers allow to override the default calling convention. Overriding
//! is not detected at the moment.
kIdHost = DETECTED_AT_COMPILE_TIME,
//! Default CDECL calling convention based on the current C++ compiler's settings.
kIdHostCDecl = DETECTED_AT_COMPILE_TIME,
//! Default STDCALL calling convention based on the current C++ compiler's settings.
//!
//! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`.
kIdHostStdCall = DETECTED_AT_COMPILE_TIME,
//! Compatibility for `__fastcall` calling convention.
//!
//! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`.
kIdHostFastCall = DETECTED_AT_COMPILE_TIME
#elif ASMJIT_ARCH_X86
kIdHost = kIdX86CDecl,
kIdHostCDecl = kIdX86CDecl,
kIdHostStdCall = kIdX86StdCall,
kIdHostFastCall = ASMJIT_CC_MSC ? kIdX86MsFastCall :
ASMJIT_CC_GCC ? kIdX86GccFastCall :
ASMJIT_CC_CLANG ? kIdX86GccFastCall : kIdNone,
kIdHostFastEval2 = kIdX86FastEval2,
kIdHostFastEval3 = kIdX86FastEval3,
kIdHostFastEval4 = kIdX86FastEval4
#elif ASMJIT_ARCH_X64
kIdHost = ASMJIT_OS_WINDOWS ? kIdX86Win64 : kIdX86SysV64,
kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host.
kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host.
kIdHostFastCall = kIdHost, // Doesn't exist, redirected to host.
kIdHostFastEval2 = kIdX64FastEval2,
kIdHostFastEval3 = kIdX64FastEval3,
kIdHostFastEval4 = kIdX64FastEval4
#elif ASMJIT_ARCH_ARM32
# if defined(__SOFTFP__)
kIdHost = kIdArm32SoftFP,
# else
kIdHost = kIdArm32HardFP,
# endif
// These don't exist on ARM.
kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host.
kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host.
kIdHostFastCall = kIdHost // Doesn't exist, redirected to host.
#else
# error "[asmjit] Couldn't determine the target's calling convention."
#endif
};
//! Calling convention algorithm.
//!
//! This is AsmJit specific. It basically describes how should AsmJit convert
//! the function arguments defined by `FuncSignature` into register ids or
//! stack offsets. The default algorithm is a standard algorithm that assigns
//! registers first, and then assigns stack. The Win64 algorithm does register
//! shadowing as defined by `WIN64` calling convention - it applies to 64-bit
//! calling conventions only.
ASMJIT_ENUM(Algorithm) {
kAlgorithmDefault = 0, //!< Default algorithm (cross-platform).
kAlgorithmWin64 = 1 //!< WIN64 specific algorithm.
};
//! Calling convention flags.
ASMJIT_ENUM(Flags) {
kFlagCalleePopsStack = 0x01, //!< Callee is responsible for cleaning up the stack.
kFlagPassFloatsByVec = 0x02, //!< Pass F32 and F64 arguments by VEC128 register.
kFlagVectorCall = 0x04, //!< This is a '__vectorcall' calling convention.
kFlagIndirectVecArgs = 0x08 //!< Pass vector arguments indirectly (as a pointer).
};
//! Internal limits of AsmJit/CallConv.
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds,
kNumRegArgsPerKind = 8
};
//! Passed registers' order.
union RegOrder {
uint8_t id[kNumRegArgsPerKind]; //!< Passed registers, ordered.
uint32_t packed[(kNumRegArgsPerKind + 3) / 4];
};
// --------------------------------------------------------------------------
// [Utilities]
// --------------------------------------------------------------------------
static ASMJIT_INLINE bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; }
static ASMJIT_INLINE bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; }
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_API Error init(uint32_t ccId) noexcept;
ASMJIT_INLINE void reset() noexcept {
::memset(this, 0, sizeof(*this));
::memset(_passedOrder, 0xFF, sizeof(_passedOrder));
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get calling convention id, see \ref Id.
ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }
//! Set calling convention id, see \ref Id.
ASMJIT_INLINE void setId(uint32_t id) noexcept { _id = static_cast<uint8_t>(id); }
//! Get architecture type.
ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archType; }
//! Set architecture type.
ASMJIT_INLINE void setArchType(uint32_t archType) noexcept { _archType = static_cast<uint8_t>(archType); }
//! Get calling convention algorithm, see \ref Algorithm.
ASMJIT_INLINE uint32_t getAlgorithm() const noexcept { return _algorithm; }
//! Set calling convention algorithm, see \ref Algorithm.
ASMJIT_INLINE void setAlgorithm(uint32_t algorithm) noexcept { _algorithm = static_cast<uint8_t>(algorithm); }
//! Get if the calling convention has the given `flag` set.
ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
//! Get calling convention flags, see \ref Flags.
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; }
//! Add calling convention flags, see \ref Flags.
ASMJIT_INLINE void setFlags(uint32_t flag) noexcept { _flags = flag; };
//! Add calling convention flags, see \ref Flags.
ASMJIT_INLINE void addFlags(uint32_t flag) noexcept { _flags |= flag; };
//! Get a natural stack alignment.
ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _naturalStackAlignment; }
//! Set a natural stack alignment.
//!
//! This function can be used to override the default stack alignment in case
//! that you know that it's alignment is different. For example it allows to
//! implement custom calling conventions that guarantee higher stack alignment.
ASMJIT_INLINE void setNaturalStackAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_naturalStackAlignment = static_cast<uint8_t>(value);
}
//! Get if this calling convention specifies 'SpillZone'.
ASMJIT_INLINE bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
//! Get size of 'SpillZone'.
ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; }
//! Set size of 'SpillZone'.
ASMJIT_INLINE void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = static_cast<uint8_t>(size); }
//! Get if this calling convention specifies 'RedZone'.
ASMJIT_INLINE bool hasRedZone() const noexcept { return _redZoneSize != 0; }
//! Get size of 'RedZone'.
ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; }
//! Set size of 'RedZone'.
ASMJIT_INLINE void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = static_cast<uint16_t>(size); }
ASMJIT_INLINE const uint8_t* getPassedOrder(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _passedOrder[kind].id;
}
ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _passedRegs[kind];
}
ASMJIT_INLINE void _setPassedPacked(uint32_t kind, uint32_t p0, uint32_t p1) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_passedOrder[kind].packed[0] = p0;
_passedOrder[kind].packed[1] = p1;
}
ASMJIT_INLINE void setPassedToNone(uint32_t kind) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_setPassedPacked(kind, ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF),
ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF));
_passedRegs[kind] = 0;
}
ASMJIT_INLINE void setPassedOrder(uint32_t kind, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_setPassedPacked(kind, ASMJIT_PACK32_4x8(a0, a1, a2, a3),
ASMJIT_PACK32_4x8(a4, a5, a6, a7));
// NOTE: This should always be called with all arguments known at compile
// time, so even if it looks scary it should be translated to a single
// instruction.
_passedRegs[kind] = (a0 != 0xFF ? 1U << a0 : 0U) |
(a1 != 0xFF ? 1U << a1 : 0U) |
(a2 != 0xFF ? 1U << a2 : 0U) |
(a3 != 0xFF ? 1U << a3 : 0U) |
(a4 != 0xFF ? 1U << a4 : 0U) |
(a5 != 0xFF ? 1U << a5 : 0U) |
(a6 != 0xFF ? 1U << a6 : 0U) |
(a7 != 0xFF ? 1U << a7 : 0U) ;
}
ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _preservedRegs[kind];
}
ASMJIT_INLINE void setPreservedRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_preservedRegs[kind] = regs;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _id; //!< Calling convention id, see \ref Id.
uint8_t _archType; //!< Architecture type (see \ref ArchInfo::Type).
uint8_t _algorithm; //!< Calling convention algorithm.
uint8_t _flags; //!< Calling convention flags.
uint8_t _naturalStackAlignment; //!< Natural stack alignment as defined by OS/ABI.
uint8_t _spillZoneSize; //!< Spill zone size (WIN64 == 32 bytes).
uint16_t _redZoneSize; //!< Red zone size (AMD64 == 128 bytes).
RegOrder _passedOrder[kMaxVRegKinds]; //!< Passed registers' order, per kind.
uint32_t _passedRegs[kMaxVRegKinds]; //!< Mask of all passed registers, per kind.
uint32_t _preservedRegs[kMaxVRegKinds];//!< Mask of all preserved registers, per kind.
};
// ============================================================================
// [asmjit::FuncArgIndex]
// ============================================================================
//! Function argument index (lo/hi).
ASMJIT_ENUM(FuncArgIndex) {
//! Maximum number of function arguments supported by AsmJit.
kFuncArgCount = 16,
//! Extended maximum number of arguments (used internally).
kFuncArgCountLoHi = kFuncArgCount * 2,
//! Index to the LO part of function argument (default).
//!
//! This value is typically omitted and added only if there is HI argument
//! accessed.
kFuncArgLo = 0,
//! Index to the HI part of function argument.
//!
//! HI part of function argument depends on target architecture. On x86 it's
//! typically used to transfer 64-bit integers (they form a pair of 32-bit
//! integers).
kFuncArgHi = kFuncArgCount
};
// ============================================================================
// [asmjit::FuncSignature]
// ============================================================================
//! Function signature.
//!
//! Contains information about function return type, count of arguments and
//! their TypeIds. Function signature is a low level structure which doesn't
//! contain platform specific or calling convention specific information.
struct FuncSignature {
enum {
//! Doesn't have variable number of arguments (`...`).
kNoVarArgs = 0xFF
};
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize the function signature.
ASMJIT_INLINE void init(uint32_t ccId, uint32_t ret, const uint8_t* args, uint32_t argCount) noexcept {
ASMJIT_ASSERT(ccId <= 0xFF);
ASMJIT_ASSERT(argCount <= 0xFF);
_callConv = static_cast<uint8_t>(ccId);
_argCount = static_cast<uint8_t>(argCount);
_vaIndex = kNoVarArgs;
_ret = ret;
_args = args;
}
ASMJIT_INLINE void reset() noexcept {
memset(this, 0, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the function's calling convention.
ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; }
//! Get if the function has variable number of arguments (...).
ASMJIT_INLINE bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
//! Get the variable arguments (...) index, `kNoVarArgs` if none.
ASMJIT_INLINE uint32_t getVAIndex() const noexcept { return _vaIndex; }
//! Get the number of function arguments.
ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; }
ASMJIT_INLINE bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
//! Get the return value type.
ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; }
//! Get the type of the argument at index `i`.
ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept {
ASMJIT_ASSERT(i < _argCount);
return _args[i];
}
//! Get the array of function arguments' types.
ASMJIT_INLINE const uint8_t* getArgs() const noexcept { return _args; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _callConv; //!< Calling convention id.
uint8_t _argCount; //!< Count of arguments.
uint8_t _vaIndex; //!< Index to a first vararg or `kNoVarArgs`.
uint8_t _ret; //!< TypeId of a return value.
const uint8_t* _args; //!< TypeIds of function arguments.
};
// ============================================================================
// [asmjit::FuncSignatureT]
// ============================================================================
//! \internal
#define T(TYPE) TypeIdOf<TYPE>::kTypeId
//! Static function signature (no arguments).
template<typename RET>
class FuncSignature0 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature0(uint32_t ccId = CallConv::kIdHost) noexcept {
init(ccId, T(RET), nullptr, 0);
}
};
//! Static function signature (1 argument).
template<typename RET, typename A0>
class FuncSignature1 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature1(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (2 arguments).
template<typename RET, typename A0, typename A1>
class FuncSignature2 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature2(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (3 arguments).
template<typename RET, typename A0, typename A1, typename A2>
class FuncSignature3 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature3(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (4 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3>
class FuncSignature4 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature4(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (5 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4>
class FuncSignature5 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature5(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (6 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
class FuncSignature6 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature6(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (7 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
class FuncSignature7 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature7(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (8 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
class FuncSignature8 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature8(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (9 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
class FuncSignature9 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature9(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (10 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
class FuncSignature10 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature10(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8), T(A9) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
#if ASMJIT_CC_HAS_VARIADIC_TEMPLATES
//! Static function signature (variadic).
template<typename RET, typename... ARGS>
class FuncSignatureT : public FuncSignature {
public:
ASMJIT_INLINE FuncSignatureT(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { (T(ARGS))... };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
#endif // ASMJIT_CC_HAS_VARIADIC_TEMPLATES
#undef T
// ============================================================================
// [asmjit::FuncSignatureX]
// ============================================================================
//! Dynamic function signature.
class FuncSignatureX : public FuncSignature {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncSignatureX(uint32_t ccId = CallConv::kIdHost) noexcept {
init(ccId, TypeId::kVoid, _builderArgList, 0);
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setCallConv(uint32_t ccId) noexcept {
ASMJIT_ASSERT(ccId <= 0xFF);
_callConv = static_cast<uint8_t>(ccId);
}
//! Set the return type to `retType`.
ASMJIT_INLINE void setRet(uint32_t retType) noexcept { _ret = retType; }
//! Set the return type based on `T`.
template<typename T>
ASMJIT_INLINE void setRetT() noexcept { setRet(TypeIdOf<T>::kTypeId); }
//! Set the argument at index `i` to the `type`
ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept {
ASMJIT_ASSERT(i < _argCount);
_builderArgList[i] = type;
}
//! Set the argument at index `i` to the type based on `T`.
template<typename T>
ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeIdOf<T>::kTypeId); }
//! Append an argument of `type` to the function prototype.
ASMJIT_INLINE void addArg(uint32_t type) noexcept {
ASMJIT_ASSERT(_argCount < kFuncArgCount);
_builderArgList[_argCount++] = static_cast<uint8_t>(type);
}
//! Append an argument of type based on `T` to the function prototype.
template<typename T>
ASMJIT_INLINE void addArgT() noexcept { addArg(TypeIdOf<T>::kTypeId); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _builderArgList[kFuncArgCount];
};
// ============================================================================
// [asmjit::FuncDetail]
// ============================================================================
//! Function detail - CallConv and expanded FuncSignature.
//!
//! Function details is architecture and OS dependent representation of function.
//! It contains calling convention and expanded function signature so all
//! arguments have assigned either register type & id or stack address.
class FuncDetail {
public:
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds
};
//! Argument or return value as defined by `FuncSignature`, but with register
//! or stack address (and other metadata) assigned.
struct Value {
ASMJIT_ENUM(Parts) {
kTypeIdShift = 24,
kTypeIdMask = 0xFF000000U,
kRegTypeShift = 8,
kRegTypeMask = 0x0000FF00U,
kRegIdShift = 0,
kRegIdMask = 0x000000FFU,
kStackOffsetShift = 0,
kStackOffsetMask = 0x0000FFFFU,
kIsByReg = 0x00010000U,
kIsByStack = 0x00020000U,
kIsIndirect = 0x00040000U
};
//! Get if this value is initialized (i.e. contains a valid data).
ASMJIT_INLINE bool isInitialized() const noexcept { return _value != 0; }
//! Initialize this in/out by a given `typeId`.
ASMJIT_INLINE void initTypeId(uint32_t typeId) noexcept { _value = typeId << kTypeIdShift; }
//! Initialize this in/out by a given `typeId`, `regType`, and `regId`.
ASMJIT_INLINE void initReg(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept {
_value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg;
}
//! Initialize this in/out by a given `typeId` and `offset`.
ASMJIT_INLINE void initStack(uint32_t typeId, uint32_t stackOffset) noexcept {
_value = (typeId << kTypeIdShift) | (stackOffset << kStackOffsetShift) | kIsByStack;
}
//! Reset the value to its uninitialized and unassigned state.
ASMJIT_INLINE void reset() noexcept { _value = 0; }
ASMJIT_INLINE void assignToReg(uint32_t regType, uint32_t regId) noexcept {
ASMJIT_ASSERT(!isAssigned());
_value |= (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg;
}
ASMJIT_INLINE void assignToStack(int32_t offset) noexcept {
ASMJIT_ASSERT(!isAssigned());
_value |= (offset << kStackOffsetShift) | kIsByStack;
}
//! Get if this argument is passed by register.
ASMJIT_INLINE bool byReg() const noexcept { return (_value & kIsByReg) != 0; }
//! Get if this argument is passed by stack.
ASMJIT_INLINE bool byStack() const noexcept { return (_value & kIsByStack) != 0; }
//! Get if this argument is passed by register.
ASMJIT_INLINE bool isAssigned() const noexcept { return (_value & (kIsByReg | kIsByStack)) != 0; }
//! Get if this argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM).
ASMJIT_INLINE bool isIndirect() const noexcept { return (_value & kIsIndirect) != 0; }
//! Get virtual type of this argument or return value.
ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; }
//! Get a register type of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; }
//! Get a physical id of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; }
//! Get a stack offset of this argument (always positive).
ASMJIT_INLINE int32_t getStackOffset() const noexcept { return (_value & kStackOffsetMask) >> kStackOffsetShift; }
uint32_t _value;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncDetail() noexcept { reset(); }
ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept {
::memcpy(this, &other, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize this `FuncDetail` to the given signature.
ASMJIT_API Error init(const FuncSignature& sign);
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Accessors - Calling Convention]
// --------------------------------------------------------------------------
//! Get the function's calling convention, see `CallConv`.
ASMJIT_INLINE const CallConv& getCallConv() const noexcept { return _callConv; }
//! Get CallConv flags, see \ref CallConv::Flags.
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _callConv.getFlags(); }
//! Check if a CallConv `flag` is set, see \ref CallConv::Flags.
ASMJIT_INLINE bool hasFlag(uint32_t ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); }
// --------------------------------------------------------------------------
// [Accessors - Arguments and Return]
// --------------------------------------------------------------------------
//! Get count of function return values.
ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; }
//! Get the number of function arguments.
ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; }
//! Get whether the function has a return value.
ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; }
//! Get function return value.
ASMJIT_INLINE Value& getRet(size_t index = 0) noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets));
return _rets[index];
}
//! Get function return value (const).
ASMJIT_INLINE const Value& getRet(size_t index = 0) const noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets));
return _rets[index];
}
//! Get function arguments array.
ASMJIT_INLINE Value* getArgs() noexcept { return _args; }
//! Get function arguments array (const).
ASMJIT_INLINE const Value* getArgs() const noexcept { return _args; }
ASMJIT_INLINE bool hasArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index].isInitialized();
}
//! Get function argument at index `index`.
ASMJIT_INLINE Value& getArg(size_t index) noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index];
}
//! Get function argument at index `index`.
ASMJIT_INLINE const Value& getArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index];
}
ASMJIT_INLINE void resetArg(size_t index) noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
_args[index].reset();
}
//! Get if the function passes one or more argument by stack.
ASMJIT_INLINE bool hasStackArgs() const noexcept { return _argStackSize != 0; }
//! Get stack size needed for function arguments passed on the stack.
ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; }
ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _callConv.getNaturalStackAlignment(); }
ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _callConv.getSpillZoneSize(); }
ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _callConv.getRedZoneSize(); }
ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { return _callConv.getPassedRegs(kind); }
ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { return _callConv.getPreservedRegs(kind); }
ASMJIT_INLINE uint32_t getUsedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _usedRegs[kind];
}
ASMJIT_INLINE void addUsedRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_usedRegs[kind] |= regs;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
CallConv _callConv; //!< Calling convention.
uint8_t _argCount; //!< Number of function arguments.
uint8_t _retCount; //!< Number of function return values.
uint32_t _usedRegs[kMaxVRegKinds]; //!< Registers that contains arguments (signature dependent).
uint32_t _argStackSize; //!< Size of arguments passed by stack.
Value _rets[2]; //!< Function return values.
Value _args[kFuncArgCountLoHi]; //!< Function arguments.
};
// ============================================================================
// [asmjit::FuncFrameInfo]
// ============================================================================
//! Function-frame information.
//!
//! This structure can be used to create a function frame in a cross-platform
//! way. It contains information about the function's stack to be used and
//! registers to be saved and restored. Based on this information in can
//! calculate the optimal layout of a function as \ref FuncFrameLayout.
struct FuncFrameInfo {
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds
};
//! Attributes.
//!
//! Attributes are designed in a way that all are initially false, and user
//! or function-frame finalizer sets them when necessary. Architecture-specific
//! attributes are prefixed with the architecture name.
ASMJIT_ENUM(Attributes) {
kAttrPreserveFP = 0x00000001U, //!< Preserve frame pointer (EBP|RBP).
kAttrCompactPE = 0x00000002U, //!< Use smaller, but possibly slower prolog/epilog.
kAttrHasCalls = 0x00000004U, //!< Function calls other functions (is not leaf).
kX86AttrAlignedVecSR = 0x00010000U, //!< Use aligned save/restore of VEC regs.
kX86AttrMmxCleanup = 0x00020000U, //!< Emit EMMS instruction in epilog (X86).
kX86AttrAvxCleanup = 0x00040000U, //!< Emit VZEROUPPER instruction in epilog (X86).
kX86AttrAvxEnabled = 0x00080000U //!< Use AVX instead of SSE for all operations (X86).
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncFrameInfo() noexcept { reset(); }
ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept {
::memcpy(this, &other, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() noexcept {
::memset(this, 0, sizeof(*this));
_stackArgsRegId = Globals::kInvalidRegId;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get frame-info flags, see \ref Attributes.
ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _attributes; }
//! Check if a frame-info `flag` is set, see \ref Attributes.
ASMJIT_INLINE bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; }
//! Add `flags` to the frame-info, see \ref Attributes.
ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _attributes |= attrs; }
//! Clear `flags` from the frame-info, see \ref Attributes.
ASMJIT_INLINE void clearAttributes(uint32_t attrs) noexcept { _attributes &= ~attrs; }
//! Get if the function preserves frame pointer (EBP|ESP on X86).
ASMJIT_INLINE bool hasPreservedFP() const noexcept { return (_attributes & kAttrPreserveFP) != 0; }
//! Enable preserved frame pointer.
ASMJIT_INLINE void enablePreservedFP() noexcept { _attributes |= kAttrPreserveFP; }
//! Disable preserved frame pointer.
ASMJIT_INLINE void disablePreservedFP() noexcept { _attributes &= ~kAttrPreserveFP; }
//! Get if the function prolog and epilog should be compacted (as small as possible).
ASMJIT_INLINE bool hasCompactPE() const noexcept { return (_attributes & kAttrCompactPE) != 0; }
//! Enable compact prolog/epilog.
ASMJIT_INLINE void enableCompactPE() noexcept { _attributes |= kAttrCompactPE; }
//! Disable compact prolog/epilog.
ASMJIT_INLINE void disableCompactPE() noexcept { _attributes &= ~kAttrCompactPE; }
//! Get if the function calls other functions.
ASMJIT_INLINE bool hasCalls() const noexcept { return (_attributes & kAttrHasCalls) != 0; }
//! Set `kFlagHasCalls` to true.
ASMJIT_INLINE void enableCalls() noexcept { _attributes |= kAttrHasCalls; }
//! Set `kFlagHasCalls` to false.
ASMJIT_INLINE void disableCalls() noexcept { _attributes &= ~kAttrHasCalls; }
//! Get if the function contains MMX cleanup - 'emms' instruction in epilog.
ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return (_attributes & kX86AttrMmxCleanup) != 0; }
//! Enable MMX cleanup.
ASMJIT_INLINE void enableMmxCleanup() noexcept { _attributes |= kX86AttrMmxCleanup; }
//! Disable MMX cleanup.
ASMJIT_INLINE void disableMmxCleanup() noexcept { _attributes &= ~kX86AttrMmxCleanup; }
//! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog.
ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return (_attributes & kX86AttrAvxCleanup) != 0; }
//! Enable AVX cleanup.
ASMJIT_INLINE void enableAvxCleanup() noexcept { _attributes |= kX86AttrAvxCleanup; }
//! Disable AVX cleanup.
ASMJIT_INLINE void disableAvxCleanup() noexcept { _attributes &= ~kX86AttrAvxCleanup; }
//! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog.
ASMJIT_INLINE bool isAvxEnabled() const noexcept { return (_attributes & kX86AttrAvxEnabled) != 0; }
//! Enable AVX cleanup.
ASMJIT_INLINE void enableAvx() noexcept { _attributes |= kX86AttrAvxEnabled; }
//! Disable AVX cleanup.
ASMJIT_INLINE void disableAvx() noexcept { _attributes &= ~kX86AttrAvxEnabled; }
//! Get which registers (by `kind`) are saved/restored in prolog/epilog, respectively.
ASMJIT_INLINE uint32_t getDirtyRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _dirtyRegs[kind];
}
//! Set which registers (by `kind`) are saved/restored in prolog/epilog, respectively.
ASMJIT_INLINE void setDirtyRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_dirtyRegs[kind] = regs;
}
//! Add registers (by `kind`) to saved/restored registers.
ASMJIT_INLINE void addDirtyRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_dirtyRegs[kind] |= regs;
}
ASMJIT_INLINE void setAllDirty() noexcept {
_dirtyRegs[0] = 0xFFFFFFFFU;
_dirtyRegs[1] = 0xFFFFFFFFU;
_dirtyRegs[2] = 0xFFFFFFFFU;
_dirtyRegs[3] = 0xFFFFFFFFU;
}
ASMJIT_INLINE void setAllDirty(uint32_t kind) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_dirtyRegs[kind] = 0xFFFFFFFFU;
}
//! Get stack-frame size used by the function.
ASMJIT_INLINE uint32_t getStackFrameSize() const noexcept { return _stackFrameSize; }
//! Get call-frame size used by the function.
ASMJIT_INLINE uint32_t getCallFrameSize() const noexcept { return _callFrameSize; }
//! Get minimum stack-frame alignment required by the function.
ASMJIT_INLINE uint32_t getStackFrameAlignment() const noexcept { return _stackFrameAlignment; }
//! Get minimum call-frame alignment required by the function.
ASMJIT_INLINE uint32_t getCallFrameAlignment() const noexcept { return _callFrameAlignment; }
ASMJIT_INLINE void setStackFrameSize(uint32_t size) noexcept { _stackFrameSize = size; }
ASMJIT_INLINE void setCallFrameSize(uint32_t size) noexcept { _callFrameSize = size; }
ASMJIT_INLINE void setStackFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_stackFrameAlignment = static_cast<uint8_t>(value);
}
ASMJIT_INLINE void setCallFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_callFrameAlignment = static_cast<uint8_t>(value);
}
ASMJIT_INLINE void mergeStackFrameSize(uint32_t size) noexcept { _stackFrameSize = std::max<uint32_t>(_stackFrameSize, size); }
ASMJIT_INLINE void mergeCallFrameSize(uint32_t size) noexcept { _callFrameSize = std::max<uint32_t>(_callFrameSize, size); }
ASMJIT_INLINE void mergeStackFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_stackFrameAlignment = static_cast<uint8_t>(std::max<uint32_t>(_stackFrameAlignment, value));
}
ASMJIT_INLINE void mergeCallFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_callFrameAlignment = static_cast<uint8_t>(std::max<uint32_t>(_callFrameAlignment, value));
}
ASMJIT_INLINE bool hasStackArgsRegId() const noexcept {
return _stackArgsRegId != Globals::kInvalidRegId;
}
ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; }
ASMJIT_INLINE void setStackArgsRegId(uint32_t regId) { _stackArgsRegId = regId; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint32_t _attributes; //!< Function attributes.
uint32_t _dirtyRegs[kMaxVRegKinds]; //!< Registers used by the function.
uint8_t _stackFrameAlignment; //!< Minimum alignment of stack-frame.
uint8_t _callFrameAlignment; //!< Minimum alignment of call-frame.
uint8_t _stackArgsRegId; //!< Register that holds base-address to arguments passed by stack.
uint32_t _stackFrameSize; //!< Size of a stack-frame used by the function.
uint32_t _callFrameSize; //!< Size of a call-frame (not part of _stackFrameSize).
};
// ============================================================================
// [asmjit::FuncFrameLayout]
// ============================================================================
//! Function-frame layout.
//!
//! Function layout is used directly by prolog and epilog insertion helpers. It
//! contains only information necessary to insert proper prolog and epilog, and
//! should be always calculated from \ref FuncDetail and \ref FuncFrameInfo, where
//! \ref FuncDetail defines function's calling convention and signature, and \ref
//! FuncFrameInfo specifies how much stack is used, and which registers are dirty.
struct FuncFrameLayout {
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds
};
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_API Error init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept;
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool hasPreservedFP() const noexcept { return static_cast<bool>(_preservedFP); }
ASMJIT_INLINE bool hasDsaSlotUsed() const noexcept { return static_cast<bool>(_dsaSlotUsed); }
ASMJIT_INLINE bool hasAlignedVecSR() const noexcept { return static_cast<bool>(_alignedVecSR); }
ASMJIT_INLINE bool hasDynamicAlignment() const noexcept { return static_cast<bool>(_dynamicAlignment); }
ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return static_cast<bool>(_mmxCleanup); }
ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return static_cast<bool>(_avxCleanup); }
ASMJIT_INLINE bool isAvxEnabled() const noexcept { return static_cast<bool>(_avxEnabled); }
ASMJIT_INLINE uint32_t getSavedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _savedRegs[kind];
}
//! Get stack size.
ASMJIT_INLINE uint32_t getStackSize() const noexcept { return _stackSize; }
//! Get stack alignment.
ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; }
//! Get the offset needed to access the function's stack (it skips call-stack).
ASMJIT_INLINE uint32_t getStackBaseOffset() const noexcept { return _stackBaseOffset; }
//! Get stack size required to save GP registers.
ASMJIT_INLINE uint32_t getGpStackSize() const noexcept { return _gpStackSize; }
//! Get stack size required to save VEC registers.
ASMJIT_INLINE uint32_t getVecStackSize() const noexcept { return _vecStackSize; }
ASMJIT_INLINE uint32_t getGpStackOffset() const noexcept { return _gpStackOffset; }
ASMJIT_INLINE uint32_t getVecStackOffset() const noexcept { return _vecStackOffset; }
ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; }
ASMJIT_INLINE uint32_t getStackArgsOffset() const noexcept { return _stackArgsOffset; }
ASMJIT_INLINE bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }
ASMJIT_INLINE uint32_t getStackAdjustment() const noexcept { return _stackAdjustment; }
ASMJIT_INLINE bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; }
ASMJIT_INLINE uint32_t getCalleeStackCleanup() const noexcept { return _calleeStackCleanup; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _stackAlignment; //!< Final stack alignment of the functions.
uint8_t _stackBaseRegId; //!< GP register that holds address of base stack address.
uint8_t _stackArgsRegId; //!< GP register that holds address of the first argument passed by stack.
uint32_t _savedRegs[kMaxVRegKinds]; //!< Registers that will be saved/restored in prolog/epilog.
uint32_t _preservedFP : 1; //!< Function preserves frame-pointer.
uint32_t _dsaSlotUsed : 1; //!< True if `_dsaSlot` contains a valid memory slot/offset.
uint32_t _alignedVecSR : 1; //!< Use instructions that perform aligned ops to save/restore XMM regs.
uint32_t _dynamicAlignment : 1; //!< Function must dynamically align the stack.
uint32_t _mmxCleanup : 1; //!< Emit 'emms' in epilog (X86).
uint32_t _avxCleanup : 1; //!< Emit 'vzeroupper' in epilog (X86).
uint32_t _avxEnabled : 1; //!< Use AVX instead of SSE for SIMD saves/restores (X86).
uint32_t _stackSize; //!< Stack size (sum of function's stack and call stack).
uint32_t _stackBaseOffset; //!< Stack offset (non-zero if kFlagHasCalls is set).
uint32_t _stackAdjustment; //!< Stack adjustment in prolog/epilog.
uint32_t _stackArgsOffset; //!< Offset to the first argument passed by stack of _stackArgsRegId.
uint32_t _dsaSlot; //!< Memory slot where the prolog inserter stores previous (unaligned) ESP.
uint16_t _calleeStackCleanup; //!< How many bytes the callee should add to the stack (X86 STDCALL).
uint16_t _gpStackSize; //!< Stack size required to save GP regs.
uint16_t _vecStackSize; //!< Stack size required to save VEC regs.
uint32_t _gpStackOffset; //!< Offset where saved GP regs are stored.
uint32_t _vecStackOffset; //!< Offset where saved GP regs are stored.
};
// ============================================================================
// [asmjit::FuncArgsMapper]
// ============================================================================
//! Assign a physical register to each function argument.
//!
//! This is used to specify where each function argument should be shuffled
//! or allocated (in case it's passed by stack).
class FuncArgsMapper {
public:
struct Value {
// NOTE: The layout is compatible with FuncDetail::Value except stack.
ASMJIT_ENUM(Parts) {
kTypeIdShift = 24,
kTypeIdMask = 0xFF000000U,
kRegTypeShift = 8,
kRegTypeMask = 0x0000FF00U,
kRegIdShift = 0,
kRegIdMask = 0x000000FFU,
kIsAssigned = 0x00010000U
};
//! Get if this value is initialized (i.e. contains a valid data).
ASMJIT_INLINE bool isAssigned() const noexcept { return _value != 0; }
//! Initialize this in/out by a given `typeId`, `regType`, and `regId`.
ASMJIT_INLINE void assign(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept {
_value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsAssigned;
}
//! Reset the value to its unassigned state.
ASMJIT_INLINE void reset() noexcept { _value = 0; }
//! Get virtual type of this argument or return value.
ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; }
//! Get a register type of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; }
//! Get a physical id of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; }
uint32_t _value;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd) noexcept { reset(fd); }
ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept {
::memcpy(this, &other, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept {
_funcDetail = fd;
::memset(_args, 0, sizeof(_args));
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE const FuncDetail* getFuncDetail() const noexcept { return _funcDetail; }
ASMJIT_INLINE void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
ASMJIT_INLINE Value& getArg(size_t index) noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
return _args[index];
}
ASMJIT_INLINE const Value& getArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
return _args[index];
}
ASMJIT_INLINE bool isAssigned(size_t index) const noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
return _args[index].isAssigned();
}
ASMJIT_INLINE void assign(size_t index, const Reg& reg, uint32_t typeId = TypeId::kVoid) noexcept {
// Not designed for virtual registers.
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
ASMJIT_ASSERT(reg.isPhysReg());
_args[index].assign(typeId, reg.getType(), reg.getId());
}
// NOTE: All `assignAll()` methods are shortcuts to assign all arguments at
// once, however, since registers are passed all at once these initializers
// don't provide any way to pass TypeId and/or to keep any argument between
// the arguments passed uninitialized.
ASMJIT_INLINE void assignAll(const Reg& a0) noexcept {
assign(0, a0);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1) noexcept {
assign(0, a0); assign(1, a1);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4); assign(5, a5);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4); assign(5, a5); assign(6, a6);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6, const Reg& a7) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4); assign(5, a5); assign(6, a6); assign(7, a7);
}
// --------------------------------------------------------------------------
// [Utilities]
// --------------------------------------------------------------------------
//! Update `FuncFrameInfo` accordingly to FuncArgsMapper.
//!
//! This method must be called if you use `FuncArgsMapper` and you plan to
//! use `FuncUtils::allocArgs()` to remap all arguments after the prolog is
//! inserted.
ASMJIT_API Error updateFrameInfo(FuncFrameInfo& ffi) const noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
const FuncDetail* _funcDetail; //!< Function detail.
Value _args[kFuncArgCountLoHi]; //!< Mapping of each function argument.
};
// ============================================================================
// [asmjit::FuncUtils]
// ============================================================================
struct FuncUtils {
ASMJIT_API static Error emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout);
ASMJIT_API static Error emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout);
ASMJIT_API static Error allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args);
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_FUNC_H
......@@ -7,24 +7,112 @@
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
// [Dependencies]
#include "../base/globals.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Assert]
// [asmjit::DebugUtils]
// ============================================================================
void assertionFailed(const char* exp, const char* file, int line) {
::fprintf(stderr, "Assertion failed: %s\n, file %s, line %d\n", exp, file, line);
#if !defined(ASMJIT_DISABLE_TEXT)
static const char errorMessages[] =
"Ok\0"
"No heap memory\0"
"No virtual memory\0"
"Invalid argument\0"
"Invalid state\0"
"Invalid architecture\0"
"Not initialized\0"
"Already initialized\0"
"Feature not enabled\0"
"Slot occupied\0"
"No code generated\0"
"Code too large\0"
"Invalid label\0"
"Label index overflow\0"
"Label already bound\0"
"Label already defined\0"
"Label name too long\0"
"Invalid label name\0"
"Invalid parent label\0"
"Non-local label can't have parent\0"
"Relocation index overflow\0"
"Invalid relocation entry\0"
"Invalid instruction\0"
"Invalid register type\0"
"Invalid register kind\0"
"Invalid register's physical id\0"
"Invalid register's virtual id\0"
"Invalid prefix combination\0"
"Invalid lock prefix\0"
"Invalid xacquire prefix\0"
"Invalid xrelease prefix\0"
"Invalid rep prefix\0"
"Invalid rex prefix\0"
"Invalid mask, expected {k}\0"
"Invalid use of {k}\0"
"Invalid use of {k}{z}\0"
"Invalid broadcast {1tox}\0"
"Invalid {er} or {sae} option\0"
"Invalid address\0"
"Invalid address index\0"
"Invalid address scale\0"
"Invalid use of 64-bit address\0"
"Invalid displacement\0"
"Invalid segment\0"
"Invalid immediate value\0"
"Invalid operand size\0"
"Ambiguous operand size\0"
"Operand size mismatch\0"
"Invalid type-info\0"
"Invalid use of a low 8-bit GPB register\0"
"Invalid use of a 64-bit GPQ register in 32-bit mode\0"
"Invalid use of an 80-bit float\0"
"Not consecutive registers\0"
"No more physical registers\0"
"Overlapped registers\0"
"Overlapping register and arguments base-address register\0"
"Unknown error\0";
#endif // ASMJIT_DISABLE_TEXT
ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
#if !defined(ASMJIT_DISABLE_TEXT)
return Utils::findPackedString(errorMessages, std::min<Error>(err, kErrorCount));
#else
static const char noMessage[] = "";
return noMessage;
#endif
}
ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept {
#if ASMJIT_OS_WINDOWS
::OutputDebugStringA(str);
#else
::fputs(str, stderr);
#endif
}
ASMJIT_FAVOR_SIZE void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept {
char str[1024];
snprintf(str, 1024,
"[asmjit] Assertion failed at %s (line %d):\n"
"[asmjit] %s\n", file, line, msg);
// Support buggy `snprintf` implementations.
str[1023] = '\0';
debugOutput(str);
::abort();
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
......@@ -8,34 +8,25 @@
#ifndef _ASMJIT_BASE_GLOBALS_H
#define _ASMJIT_BASE_GLOBALS_H
// [Dependencies - AsmJit]
#include "../build.h"
// [Dependencies]
#include "../asmjit_build.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Ptr / SignedPtr]
// [asmjit::Globals]
// ============================================================================
//! 64-bit unsigned pointer, compatible with JIT and non-JIT generators.
//!
//! This is the preferred pointer type to use with AsmJit library. It has a
//! capability to hold any pointer for any architecture making it an ideal
//! candidate for cross-platform code generation.
typedef uint64_t Ptr;
//! 64-bit signed pointer, like \ref Ptr, but made signed.
typedef int64_t SignedPtr;
enum { kInvalidValue = 0xFFFFFFFFU };
// ============================================================================
// [asmjit::GlobalDefs]
// ============================================================================
//! AsmJit globals.
namespace Globals {
//! Invalid index
//!
......@@ -45,133 +36,306 @@ typedef int64_t SignedPtr;
static const size_t kInvalidIndex = ~static_cast<size_t>(0);
//! Invalid base address.
static const Ptr kNoBaseAddress = static_cast<Ptr>(static_cast<SignedPtr>(-1));
static const uint64_t kNoBaseAddress = ~static_cast<uint64_t>(0);
//! Global constants.
ASMJIT_ENUM(GlobalDefs) {
//! Invalid value or operand id.
kInvalidValue = 0xFFFFFFFF,
//! Invalid register index.
kInvalidReg = 0xFF,
//! Invalid variable type.
kInvalidVar = 0xFF,
//! Global definitions.
ASMJIT_ENUM(Defs) {
//! Invalid register id.
kInvalidRegId = 0xFF,
//! Host memory allocator overhead.
//!
//! The overhead is decremented from all zone allocators so the operating
//! system doesn't have allocate extra virtual page to keep tract of the
//! requested memory block.
//!
//! The number is actually a guess.
kMemAllocOverhead = sizeof(intptr_t) * 4,
kAllocOverhead = static_cast<int>(sizeof(intptr_t) * 4),
//! Aggressive growing strategy threshold.
kAllocThreshold = 8192 * 1024
};
ASMJIT_ENUM(Limits) {
//! Count of register kinds that are important to Function API and CodeCompiler.
//! The target architecture can define more register kinds for special registers,
//! but these will never map to virtual registers and will never be used to pass
//! and return function arguments and function return values, respectively.
kMaxVRegKinds = 4,
//! Memory grow threshold.
//! Maximum number of physical registers of all kinds of all supported
//! architectures. This is only important for \ref CodeCompiler and its
//! \ref RAPass (register allocator pass).
//!
//! After the grow threshold is reached the capacity won't be doubled
//! anymore.
kMemAllocGrowMax = 8192 * 1024
//! NOTE: The distribution of these registers is architecture specific.
kMaxPhysRegs = 64,
//! Maximum alignment.
kMaxAlignment = 64,
//! Maximum label or symbol length in bytes (take into consideration that a
//! single UTF-8 character can take more than single byte to encode it).
kMaxLabelLength = 2048
};
} // Globals namespace
// ============================================================================
// [asmjit::ArchId]
// [asmjit::Error]
// ============================================================================
//! CPU architecture identifier.
ASMJIT_ENUM(ArchId) {
//! No/Unknown architecture.
kArchNone = 0,
//! AsmJit error type (uint32_t).
typedef uint32_t Error;
//! X86 architecture.
kArchX86 = 1,
//! X64 architecture, also called AMD64.
kArchX64 = 2,
//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Arm architecture.
kArchArm = 4,
//! Heap memory allocation failed.
kErrorNoHeapMemory,
#if defined(ASMJIT_ARCH_X86)
kArchHost = kArchX86,
#endif // ASMJIT_ARCH_X86
//! Virtual memory allocation failed.
kErrorNoVirtualMemory,
#if defined(ASMJIT_ARCH_X64)
kArchHost = kArchX64,
#endif // ASMJIT_ARCH_X64
//! Invalid argument.
kErrorInvalidArgument,
#if defined(ASMJIT_ARCH_ARM)
kArchHost = kArchArm,
#endif // ASMJIT_ARCH_ARM
//! Invalid state.
//!
//! If this error is returned it means that either you are doing something
//! wrong or AsmJit caught itself by doing something wrong. This error should
//! not be underestimated.
kErrorInvalidState,
//! Whether the host is 64-bit.
kArchHost64Bit = sizeof(intptr_t) >= 8
};
//! Invalid or incompatible architecture.
kErrorInvalidArch,
//! \}
//! The object is not initialized.
kErrorNotInitialized,
//! The object is already initialized.
kErrorAlreadyInitialized,
//! Built-in feature was disabled at compile time and it's not available.
kErrorFeatureNotEnabled,
//! CodeHolder can't have attached more than one \ref Assembler at a time.
kErrorSlotOccupied,
//! No code generated.
//!
//! Returned by runtime if the \ref CodeHolder contains no code.
kErrorNoCodeGenerated,
//! Code generated is larger than allowed.
kErrorCodeTooLarge,
//! Attempt to use uninitialized label.
kErrorInvalidLabel,
//! Label index overflow - a single `Assembler` instance can hold more than
//! 2 billion labels (2147483391 to be exact). If there is an attempt to
//! create more labels this error is returned.
kErrorLabelIndexOverflow,
//! Label is already bound.
kErrorLabelAlreadyBound,
//! Label is already defined (named labels).
kErrorLabelAlreadyDefined,
//! Label name is too long.
kErrorLabelNameTooLong,
//! Label must always be local if it's anonymous (without a name).
kErrorInvalidLabelName,
//! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid.
kErrorInvalidParentLabel,
//! Parent id specified for a non-local (global) label.
kErrorNonLocalLabelCantHaveParent,
//! Relocation index overflow.
kErrorRelocIndexOverflow,
//! Invalid relocation entry.
kErrorInvalidRelocEntry,
//! Invalid instruction.
kErrorInvalidInstruction,
//! Invalid register type.
kErrorInvalidRegType,
//! Invalid register kind.
kErrorInvalidRegKind,
//! Invalid register's physical id.
kErrorInvalidPhysId,
//! Invalid register's virtual id.
kErrorInvalidVirtId,
//! Invalid prefix combination.
kErrorInvalidPrefixCombination,
//! Invalid LOCK prefix.
kErrorInvalidLockPrefix,
//! Invalid XACQUIRE prefix.
kErrorInvalidXAcquirePrefix,
//! Invalid XACQUIRE prefix.
kErrorInvalidXReleasePrefix,
//! Invalid REP prefix.
kErrorInvalidRepPrefix,
//! Invalid REX prefix.
kErrorInvalidRexPrefix,
//! Invalid mask register (not 'k').
kErrorInvalidKMaskReg,
//! Invalid {k} use (not supported by the instruction).
kErrorInvalidKMaskUse,
//! Invalid {k}{z} use (not supported by the instruction).
kErrorInvalidKZeroUse,
//! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}.
kErrorInvalidBroadcast,
//! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512).
kErrorInvalidEROrSAE,
//! Invalid address used (not encodable).
kErrorInvalidAddress,
//! Invalid index register used in memory address (not encodable).
kErrorInvalidAddressIndex,
//! Invalid address scale (not encodable).
kErrorInvalidAddressScale,
//! Invalid use of 64-bit address.
kErrorInvalidAddress64Bit,
//! Invalid displacement (not encodable).
kErrorInvalidDisplacement,
//! Invalid segment (X86).
kErrorInvalidSegment,
//! Invalid immediate (out of bounds on X86 and invalid pattern on ARM).
kErrorInvalidImmediate,
//! Invalid operand size.
kErrorInvalidOperandSize,
//! Ambiguous operand size (memory has zero size while it's required to determine the operation type.
kErrorAmbiguousOperandSize,
//! Mismatching operand size (size of multiple operands doesn't match the operation size).
kErrorOperandSizeMismatch,
//! Invalid TypeId.
kErrorInvalidTypeId,
//! Invalid use of a 8-bit GPB-HIGH register.
kErrorInvalidUseOfGpbHi,
//! Invalid use of a 64-bit GPQ register in 32-bit mode.
kErrorInvalidUseOfGpq,
//! Invalid use of an 80-bit float (TypeId::kF80).
kErrorInvalidUseOfF80,
//! Some registers in the instruction muse be consecutive (some ARM and AVX512 neural-net instructions).
kErrorNotConsecutiveRegs,
//! AsmJit requires a physical register, but no one is available.
kErrorNoMorePhysRegs,
//! A variable has been assigned more than once to a function argument (CodeCompiler).
kErrorOverlappedRegs,
//! Invalid register to hold stack arguments offset.
kErrorOverlappingStackRegWithRegArg,
//! Count of AsmJit error codes.
kErrorCount
};
// ============================================================================
// [asmjit::Init / NoInit]
// [asmjit::Internal]
// ============================================================================
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
namespace Internal {
#if defined(ASMJIT_CUSTOM_ALLOC) && \
defined(ASMJIT_CUSTOM_REALLOC) && \
defined(ASMJIT_CUSTOM_FREE)
static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ASMJIT_CUSTOM_ALLOC(size); }
static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ASMJIT_CUSTOM_REALLOC(p, size); }
static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ASMJIT_CUSTOM_FREE(p); }
#elif !defined(ASMJIT_CUSTOM_ALLOC) && \
!defined(ASMJIT_CUSTOM_REALLOC) && \
!defined(ASMJIT_CUSTOM_FREE)
static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ::malloc(size); }
static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ::realloc(p, size); }
static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ::free(p); }
#else
# error "[asmjit] You must provide either none or all of ASMJIT_CUSTOM_[ALLOC|REALLOC|FREE]"
#endif
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
//! Cast designed to cast between function and void* pointers.
template<typename Dst, typename Src>
static ASMJIT_INLINE Dst ptr_cast(Src p) noexcept { return (Dst)p; }
} // Internal namespace
template<typename Func>
static ASMJIT_INLINE Func ptr_as_func(void* func) noexcept { return Internal::ptr_cast<Func, void*>(func); }
template<typename Func>
static ASMJIT_INLINE void* func_as_ptr(Func func) noexcept { return Internal::ptr_cast<void*, Func>(func); }
// ============================================================================
// [asmjit::Assert]
// [asmjit::DebugUtils]
// ============================================================================
//! \addtogroup asmjit_base_general
//! \{
namespace DebugUtils {
//! Called in debug build on assertion failure.
//! Returns the error `err` passed.
//!
//! Provided for debugging purposes. Putting a breakpoint inside `errored` can
//! help with tracing the origin of any error reported / returned by AsmJit.
static ASMJIT_INLINE Error errored(Error err) noexcept { return err; }
//! Get a printable version of `asmjit::Error` code.
ASMJIT_API const char* errorAsString(Error err) noexcept;
//! Called to output debugging message(s).
ASMJIT_API void debugOutput(const char* str) noexcept;
//! Called on assertion failure.
//!
//! \param exp Expression that failed.
//! \param file Source file name where it happened.
//! \param line Line in the source file.
//! \param msg Message to display.
//!
//! If you have problems with assertions put a breakpoint at assertionFailed()
//! function (asmjit/base/globals.cpp) and check the call stack to locate the
//! failing code.
ASMJIT_API void assertionFailed(const char* exp, const char* file, int line);
ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept;
#if defined(ASMJIT_DEBUG)
#define ASMJIT_ASSERT(_Exp_) \
do { \
if (!(_Exp_)) ::asmjit::assertionFailed(#_Exp_, __FILE__, __LINE__); \
# define ASMJIT_ASSERT(exp) \
do { \
if (ASMJIT_LIKELY(exp)) \
break; \
::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #exp); \
} while (0)
# define ASMJIT_NOT_REACHED() \
do { \
::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, \
"ASMJIT_NOT_REACHED has been reached"); \
ASMJIT_ASSUME(0); \
} while (0)
#else
#define ASMJIT_ASSERT(_Exp_) ASMJIT_NOP()
# define ASMJIT_ASSERT(exp) ASMJIT_NOP
# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0)
#endif // DEBUG
//! \}
//! \internal
//!
//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller.
#define ASMJIT_PROPAGATE(...) \
do { \
::asmjit::Error _err = __VA_ARGS__; \
if (ASMJIT_UNLIKELY(_err)) \
return _err; \
} while (0)
} // asmjit namespace
} // DebugUtils namespace
// ============================================================================
// [asmjit_cast<>]
// [asmjit::Init / NoInit]
// ============================================================================
//! \addtogroup asmjit_base_util
//! \{
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
//! Cast used to cast pointer to function. It's like reinterpret_cast<>,
//! but uses internally C style cast to work with MinGW.
//!
//! If you are using single compiler and `reinterpret_cast<>` works for you,
//! there is no reason to use `asmjit_cast<>`. If you are writing
//! cross-platform software with various compiler support, consider using
//! `asmjit_cast<>` instead of `reinterpret_cast<>`.
template<typename T, typename Z>
static ASMJIT_INLINE T asmjit_cast(Z* p) { return (T)p; }
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_GLOBALS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../asmjit_build.h"
#if defined(ASMJIT_BUILD_X86)
// [Dependencies]
#include "../base/arch.h"
#include "../base/inst.h"
#if defined(ASMJIT_BUILD_X86)
# include "../x86/x86instimpl_p.h"
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
# include "../arm/arminstimpl_p.h"
#endif // ASMJIT_BUILD_ARM
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Inst - Validate]
// ============================================================================
#if !defined(ASMJIT_DISABLE_VALIDATION)
Error Inst::validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept {
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86InstImpl::validate(archType, detail, operands, count);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInstImpl::validate(archType, detail, operands, count);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
#endif
// ============================================================================
// [asmjit::Inst - CheckFeatures]
// ============================================================================
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
Error Inst::checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept {
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86InstImpl::checkFeatures(archType, detail, operands, count, out);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInstImpl::checkFeatures(archType, detail, operands, count, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
#endif // !defined(ASMJIT_DISABLE_EXTENSIONS)
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // ASMJIT_BUILD_X86
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_INST_H
#define _ASMJIT_BASE_INST_H
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/operand.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Inst]
// ============================================================================
//! Definitions and utilities related to instructions used by all architectures.
struct Inst {
ASMJIT_ENUM(Id) {
kIdNone = 0 //!< Invalid or uninitialized instruction id.
};
//! Describes an instruction's jump type, if any.
ASMJIT_ENUM(JumpType) {
kJumpTypeNone = 0, //!< Instruction doesn't jump (regular instruction).
kJumpTypeDirect = 1, //!< Instruction is a unconditional (direct) jump.
kJumpTypeConditional = 2, //!< Instruction is a conditional jump.
kJumpTypeCall = 3, //!< Instruction is a function call.
kJumpTypeReturn = 4 //!< Instruction is a function return.
};
// --------------------------------------------------------------------------
// [Detail]
// --------------------------------------------------------------------------
//! Instruction id, options, and extraReg packed in a single structure. This
//! structure exists to simplify analysis and validation API that requires a
//! lot of information about the instruction to be processed.
class Detail {
public:
ASMJIT_INLINE Detail() noexcept
: instId(0),
options(0),
extraReg() {}
explicit ASMJIT_INLINE Detail(uint32_t instId, uint32_t options = 0) noexcept
: instId(instId),
options(options),
extraReg() {}
ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const RegOnly& reg) noexcept
: instId(instId),
options(options),
extraReg(reg) {}
ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const Reg& reg) noexcept
: instId(instId),
options(options) { extraReg.init(reg); }
// ------------------------------------------------------------------------
// [Accessors]
// ------------------------------------------------------------------------
ASMJIT_INLINE bool hasExtraReg() const noexcept { return extraReg.isValid(); }
// ------------------------------------------------------------------------
// [Members]
// ------------------------------------------------------------------------
uint32_t instId;
uint32_t options;
RegOnly extraReg;
};
// --------------------------------------------------------------------------
// [API]
// --------------------------------------------------------------------------
#if !defined(ASMJIT_DISABLE_VALIDATION)
//! Validate the given instruction.
ASMJIT_API static Error validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept;
#endif // !ASMJIT_DISABLE_VALIDATION
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
//! Check CPU features required to execute the given instruction.
ASMJIT_API static Error checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept;
#endif // !defined(ASMJIT_DISABLE_EXTENSIONS)
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_INST_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
#if defined(ASMJIT_TEST)
UNIT(base_intutil) {
uint32_t i;
INFO("IntTraits<>.");
EXPECT(IntTraits<signed char>::kIsSigned,
"IntTraits<signed char> should report signed.");
EXPECT(IntTraits<unsigned char>::kIsUnsigned,
"IntTraits<unsigned char> should report unsigned.");
EXPECT(IntTraits<signed short>::kIsSigned,
"IntTraits<signed short> should report signed.");
EXPECT(IntTraits<unsigned short>::kIsUnsigned,
"IntTraits<unsigned short> should report unsigned.");
EXPECT(IntTraits<int>::kIsSigned,
"IntTraits<int> should report signed.");
EXPECT(IntTraits<unsigned int>::kIsUnsigned,
"IntTraits<unsigned int> should report unsigned.");
EXPECT(IntTraits<long>::kIsSigned,
"IntTraits<long> should report signed.");
EXPECT(IntTraits<unsigned long>::kIsUnsigned,
"IntTraits<unsigned long> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsSigned,
"IntTraits<intptr_t> should report signed.");
EXPECT(IntTraits<uintptr_t>::kIsUnsigned,
"IntTraits<uintptr_t> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsIntPtr,
"IntTraits<intptr_t> should report intptr_t type.");
EXPECT(IntTraits<uintptr_t>::kIsIntPtr,
"IntTraits<uintptr_t> should report intptr_t type.");
INFO("IntUtil::iMin()/iMax().");
EXPECT(IntUtil::iMin<int>(0, -1) == -1,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMin<int>(-1, -2) == -2,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMin<int>(1, 2) == 1,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMax<int>(0, -1) == 0,
"IntUtil::iMax<int> should return a maximum value.");
EXPECT(IntUtil::iMax<int>(-1, -2) == -1,
"IntUtil::iMax<int> should return a maximum value.");
EXPECT(IntUtil::iMax<int>(1, 2) == 2,
"IntUtil::iMax<int> should return a maximum value.");
INFO("IntUtil::inInterval().");
EXPECT(IntUtil::inInterval<int>(11, 10, 20) == true,
"IntUtil::inInterval<int> should return true if inside.");
EXPECT(IntUtil::inInterval<int>(101, 10, 20) == false,
"IntUtil::inInterval<int> should return false if outside.");
INFO("IntUtil::isInt8().");
EXPECT(IntUtil::isInt8(-128) == true,
"IntUtil::isInt8<> should return true if inside.");
EXPECT(IntUtil::isInt8(127) == true,
"IntUtil::isInt8<> should return true if inside.");
EXPECT(IntUtil::isInt8(-129) == false,
"IntUtil::isInt8<> should return false if outside.");
EXPECT(IntUtil::isInt8(128) == false,
"IntUtil::isInt8<> should return false if outside.");
INFO("IntUtil::isUInt8().");
EXPECT(IntUtil::isUInt8(255) == true,
"IntUtil::isUInt8<> should return true if inside.");
EXPECT(IntUtil::isUInt8(256) == false,
"IntUtil::isUInt8<> should return false if outside.");
EXPECT(IntUtil::isUInt8(-1) == false,
"IntUtil::isUInt8<> should return false if negative.");
INFO("IntUtil::isInt16().");
EXPECT(IntUtil::isInt16(-32768) == true,
"IntUtil::isInt16<> should return true if inside.");
EXPECT(IntUtil::isInt16(32767) == true,
"IntUtil::isInt16<> should return true if inside.");
EXPECT(IntUtil::isInt16(-32769) == false,
"IntUtil::isInt16<> should return false if outside.");
EXPECT(IntUtil::isInt16(32768) == false,
"IntUtil::isInt16<> should return false if outside.");
INFO("IntUtil::isUInt16().");
EXPECT(IntUtil::isUInt16(65535) == true,
"IntUtil::isUInt16<> should return true if inside.");
EXPECT(IntUtil::isUInt16(65536) == false,
"IntUtil::isUInt16<> should return false if outside.");
EXPECT(IntUtil::isUInt16(-1) == false,
"IntUtil::isUInt16<> should return false if negative.");
INFO("IntUtil::isInt32().");
EXPECT(IntUtil::isInt32(2147483647) == true,
"IntUtil::isInt32<int> should return true if inside.");
EXPECT(IntUtil::isInt32(-2147483647 - 1) == true,
"IntUtil::isInt32<int> should return true if inside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(2147483648)) == false,
"IntUtil::isInt32<int> should return false if outside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false,
"IntUtil::isInt32<int> should return false if outside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false,
"IntUtil::isInt32<int> should return false if outside.");
INFO("IntUtil::isUInt32().");
EXPECT(IntUtil::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true,
"IntUtil::isUInt32<int> should return true if inside.");
EXPECT(IntUtil::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false,
"IntUtil::isUInt32<int> should return false if outside.");
EXPECT(IntUtil::isUInt32(-1) == false,
"IntUtil::isUInt32<int> should return false if negative.");
INFO("IntUtil::isPower2().");
for (i = 0; i < 64; i++) {
EXPECT(IntUtil::isPowerOf2(static_cast<uint64_t>(1) << i) == true,
"IntUtil::isPower2() didn't report power of 2.");
EXPECT(IntUtil::isPowerOf2((static_cast<uint64_t>(1) << i) ^ 0x001101) == false,
"IntUtil::isPower2() didn't report not power of 2.");
}
INFO("IntUtil::mask().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::mask(i) == (1 << i),
"IntUtil::mask(%u) should return %X.", i, (1 << i));
}
INFO("IntUtil::bits().");
for (i = 0; i < 32; i++) {
uint32_t expectedBits = 0;
for (uint32_t b = 0; b < i; b++)
expectedBits |= static_cast<uint32_t>(1) << b;
EXPECT(IntUtil::bits(i) == expectedBits,
"IntUtil::bits(%u) should return %X.", i, expectedBits);
}
INFO("IntUtil::hasBit().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::hasBit((1 << i), i) == true,
"IntUtil::hasBit(%X, %u) should return true.", (1 << i), i);
}
INFO("IntUtil::bitCount().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::bitCount((1 << i)) == 1,
"IntUtil::bitCount(%X) should return true.", (1 << i));
}
EXPECT(IntUtil::bitCount(0x000000F0) == 4, "");
EXPECT(IntUtil::bitCount(0x10101010) == 4, "");
EXPECT(IntUtil::bitCount(0xFF000000) == 8, "");
EXPECT(IntUtil::bitCount(0xFFFFFFF7) == 31, "");
EXPECT(IntUtil::bitCount(0x7FFFFFFF) == 31, "");
INFO("IntUtil::findFirstBit().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::findFirstBit((1 << i)) == i,
"IntUtil::findFirstBit(%X) should return %u.", (1 << i), i);
}
INFO("IntUtil::isAligned().");
EXPECT(IntUtil::isAligned<size_t>(0xFFFF, 4) == false, "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF4, 4) == true , "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF8, 8) == true , "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF0, 16) == true , "");
INFO("IntUtil::alignTo().");
EXPECT(IntUtil::alignTo<size_t>(0xFFFF, 4) == 0x10000, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF4, 4) == 0x0FFF4, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF8, 8) == 0x0FFF8, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF0, 16) == 0x0FFF0, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF0, 32) == 0x10000, "");
INFO("IntUtil::alignToPowerOf2().");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0xFFFF) == 0x10000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0xF123) == 0x10000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x0F00) == 0x01000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x0100) == 0x00100, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x1001) == 0x02000, "");
INFO("IntUtil::deltaTo().");
EXPECT(IntUtil::deltaTo<size_t>(0xFFFF, 4) == 1, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF4, 4) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF8, 8) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF0, 16) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF0, 32) == 16, "");
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment