[Doc] Mostly documentation update

This commit is contained in:
kobalicek
2024-01-03 17:03:06 +01:00
parent 3772c447ca
commit 33ef5fded9
13 changed files with 192 additions and 99 deletions

View File

@@ -966,12 +966,15 @@ namespace asmjit {
//! with assembler requires the knowledge of the following: //! with assembler requires the knowledge of the following:
//! //!
//! - \ref BaseAssembler and architecture-specific assemblers: //! - \ref BaseAssembler and architecture-specific assemblers:
//! - \ref x86::Assembler - Assembler specific to X86 architecture //! - \ref x86::Assembler - Assembler implementation targeting X86 and X86_64 architectures.
//! - \ref a64::Assembler - Assembler implementation targeting AArch64 architecture.
//! - \ref Operand and its variations: //! - \ref Operand and its variations:
//! - \ref BaseReg - Base class for a register operand, inherited by: //! - \ref BaseReg - Base class for a register operand, inherited by:
//! - \ref x86::Reg - Register operand specific to X86 architecture. //! - \ref x86::Reg - Register operand specific to X86 and X86_64 architectures.
//! - \ref arm::Reg - Register operand specific to AArch64 architecture.
//! - \ref BaseMem - Base class for a memory operand, inherited by: //! - \ref BaseMem - Base class for a memory operand, inherited by:
//! - \ref x86::Mem - Memory operand specific to X86 architecture. //! - \ref x86::Mem - Memory operand specific to X86 architecture.
//! - \ref arm::Mem - Memory operand specific to AArch64 architecture.
//! - \ref Imm - Immediate (value) operand. //! - \ref Imm - Immediate (value) operand.
//! - \ref Label - Label operand. //! - \ref Label - Label operand.
//! //!
@@ -1150,10 +1153,10 @@ namespace asmjit {
//! //!
//! void testX86Mem() { //! void testX86Mem() {
//! // The same as: dword ptr [rax + rbx]. //! // The same as: dword ptr [rax + rbx].
//! x86::Mem a = x86::dword_ptr(rax, rbx); //! x86::Mem a = x86::dword_ptr(x86::rax, x86::rbx);
//! //!
//! // The same as: qword ptr [rdx + rsi << 0 + 1]. //! // The same as: qword ptr [rdx + rsi << 0 + 1].
//! x86::Mem b = x86::qword_ptr(rdx, rsi, 0, 1); //! x86::Mem b = x86::qword_ptr(x86::rdx, x86::rsi, 0, 1);
//! } //! }
//! ``` //! ```
//! //!
@@ -1166,7 +1169,7 @@ namespace asmjit {
//! //!
//! void testX86Mem() { //! void testX86Mem() {
//! // The same as: dword ptr [rax + 12]. //! // The same as: dword ptr [rax + 12].
//! x86::Mem mem = x86::dword_ptr(rax, 12); //! x86::Mem mem = x86::dword_ptr(x86::rax, 12);
//! //!
//! mem.hasBase(); // true. //! mem.hasBase(); // true.
//! mem.hasIndex(); // false. //! mem.hasIndex(); // false.
@@ -1176,8 +1179,8 @@ namespace asmjit {
//! mem.setSize(0); // Sets the size to 0 (makes it size-less). //! mem.setSize(0); // Sets the size to 0 (makes it size-less).
//! mem.addOffset(-1); // Adds -1 to the offset and makes it 11. //! mem.addOffset(-1); // Adds -1 to the offset and makes it 11.
//! mem.setOffset(0); // Sets the offset to 0. //! mem.setOffset(0); // Sets the offset to 0.
//! mem.setBase(rcx); // Changes BASE to RCX. //! mem.setBase(x86::rcx); // Changes BASE to RCX.
//! mem.setIndex(rax); // Changes INDEX to RAX. //! mem.setIndex(x86::rax); // Changes INDEX to RAX.
//! mem.hasIndex(); // true. //! mem.hasIndex(); // true.
//! } //! }
//! // ... //! // ...
@@ -1269,7 +1272,8 @@ namespace asmjit {
//! //!
//! ### Builder Examples //! ### Builder Examples
//! //!
//! - \ref x86::Builder provides many X86/X64 examples. //! - \ref x86::Builder - Builder implementation targeting X86 and X86_64 architectures.
//! - \ref a64::Builder - Builder implementation targeting AArch64 architecture.
//! \defgroup asmjit_compiler Compiler //! \defgroup asmjit_compiler Compiler
@@ -1306,7 +1310,8 @@ namespace asmjit {
//! //!
//! ### Compiler Examples //! ### Compiler Examples
//! //!
//! - \ref x86::Compiler provides many X86/X64 examples. //! - \ref x86::Compiler - Compiler implementation targeting X86 and X86_64 architectures.
//! - \ref a64::Compiler - Compiler implementation targeting AArch64 architecture.
//! //!
//! ### Compiler Tips //! ### Compiler Tips
//! //!
@@ -1478,7 +1483,7 @@ namespace asmjit {
//! The first example illustrates how to format operands: //! The first example illustrates how to format operands:
//! //!
//! ``` //! ```
//! #include <asmjit/core.h> //! #include <asmjit/x86.h>
//! #include <stdio.h> //! #include <stdio.h>
//! //!
//! using namespace asmjit; //! using namespace asmjit;
@@ -1503,17 +1508,17 @@ namespace asmjit {
//! // compatible with what AsmJit normally does. //! // compatible with what AsmJit normally does.
//! Arch arch = Arch::kX64; //! Arch arch = Arch::kX64;
//! //!
//! log(arch, rax); // Prints 'rax'. //! logOperand(arch, rax); // Prints 'rax'.
//! log(arch, ptr(rax, rbx, 2)); // Prints '[rax + rbx * 4]`. //! logOperand(arch, ptr(rax, rbx, 2)); // Prints '[rax + rbx * 4]`.
//! log(arch, dword_ptr(rax, rbx, 2)); // Prints 'dword [rax + rbx * 4]`. //! logOperand(arch, dword_ptr(rax, rbx, 2)); // Prints 'dword [rax + rbx * 4]`.
//! log(arch, imm(42)); // Prints '42'. //! logOperand(arch, imm(42)); // Prints '42'.
//! } //! }
//! ``` //! ```
//! //!
//! Next example illustrates how to format whole instructions: //! Next example illustrates how to format whole instructions:
//! //!
//! ``` //! ```
//! #include <asmjit/core.h> //! #include <asmjit/x86.h>
//! #include <stdio.h> //! #include <stdio.h>
//! #include <utility> //! #include <utility>
//! //!
@@ -1528,7 +1533,7 @@ namespace asmjit {
//! FormatFlags formatFlags = FormatFlags::kNone; //! FormatFlags formatFlags = FormatFlags::kNone;
//! //!
//! // The formatter expects operands in an array. //! // The formatter expects operands in an array.
//! Operand_ operands { std::forward<Args>(args)... }; //! Operand_ operands[] { std::forward<Args>(args)... };
//! //!
//! StringTmp<128> sb; //! StringTmp<128> sb;
//! Formatter::formatInstruction( //! Formatter::formatInstruction(
@@ -1550,13 +1555,13 @@ namespace asmjit {
//! // Prints 'vaddpd zmm0, zmm1, [rax] {1to8}'. //! // Prints 'vaddpd zmm0, zmm1, [rax] {1to8}'.
//! logInstruction(arch, //! logInstruction(arch,
//! BaseInst(Inst::kIdVaddpd), //! BaseInst(Inst::kIdVaddpd),
//! zmm0, zmm1, ptr(rax)._1toN()); //! zmm0, zmm1, ptr(rax)._1to8());
//! //!
//! // BaseInst abstracts instruction id, instruction options, and extraReg. //! // BaseInst abstracts instruction id, instruction options, and extraReg.
//! // Prints 'lock add [rax], rcx'. //! // Prints 'lock add [rax], rcx'.
//! logInstruction(arch, //! logInstruction(arch,
//! BaseInst(Inst::kIdAdd, InstOptions::kX86_Lock), //! BaseInst(Inst::kIdAdd, InstOptions::kX86_Lock),
//! x86::ptr(rax), rcx); //! ptr(rax), rcx);
//! //!
//! // Similarly an extra register (like AVX-512 selector) can be used. //! // Similarly an extra register (like AVX-512 selector) can be used.
//! // Prints 'vaddpd zmm0 {k2} {z}, zmm1, [rax]'. //! // Prints 'vaddpd zmm0 {k2} {z}, zmm1, [rax]'.

View File

@@ -13,6 +13,7 @@
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_builder //! \addtogroup asmjit_builder
//! \{ //! \{
@@ -28,6 +29,7 @@ static inline void BaseBuilder_assignInstState(BaseBuilder* self, InstNode* node
} }
//! \} //! \}
//! \endcond
ASMJIT_END_NAMESPACE ASMJIT_END_NAMESPACE

View File

@@ -132,8 +132,8 @@ CodeHolder::~CodeHolder() noexcept {
CodeHolder_resetInternal(this, ResetPolicy::kHard); CodeHolder_resetInternal(this, ResetPolicy::kHard);
} }
// CodeHolder - Init & Reset // CodeHolder - Initialization & Reset
// ========================= // ===================================
inline void CodeHolder_setSectionDefaultName( inline void CodeHolder_setSectionDefaultName(
Section* section, Section* section,

View File

@@ -1110,7 +1110,7 @@ public:
//! \} //! \}
//! \name Init & Reset //! \name Initialization & Reset
//! \{ //! \{
//! Initializes CpuInfo architecture and sub-architecture members to `arch` and `subArch`, respectively. //! Initializes CpuInfo architecture and sub-architecture members to `arch` and `subArch`, respectively.

View File

@@ -337,10 +337,17 @@ public:
//! Tests whether the emitter is destroyed (only used during destruction). //! Tests whether the emitter is destroyed (only used during destruction).
ASMJIT_INLINE_NODEBUG bool isDestroyed() const noexcept { return hasEmitterFlag(EmitterFlags::kDestroyed); } ASMJIT_INLINE_NODEBUG bool isDestroyed() const noexcept { return hasEmitterFlag(EmitterFlags::kDestroyed); }
//! \}
//! \cond INTERNAL
//! \name Internal Functions
//! \{
ASMJIT_INLINE_NODEBUG void _addEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags |= flags; } ASMJIT_INLINE_NODEBUG void _addEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags |= flags; }
ASMJIT_INLINE_NODEBUG void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; } ASMJIT_INLINE_NODEBUG void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; }
//! \} //! \}
//! \endcond
//! \name Target Information //! \name Target Information
//! \{ //! \{
@@ -554,23 +561,38 @@ public:
//! \name Emitter State //! \name Emitter State
//! \{ //! \{
//! Resets the emitter state, which contains instruction options, extra register, and inline comment.
//!
//! Emitter can have a state that describes instruction options and extra register used by the instruction. Most
//! instructions don't need nor use the state, however, if an instruction uses a prefix such as REX or REP prefix,
//! which is set explicitly, then the state would contain it. This allows to mimic the syntax of assemblers such
//! as X86. For example `rep().movs(...)` would map to a `REP MOVS` instuction on X86. The same applies to various
//! hints and the use of a mask register in AVX-512 mode.
ASMJIT_INLINE_NODEBUG void resetState() noexcept { ASMJIT_INLINE_NODEBUG void resetState() noexcept {
resetInstOptions(); resetInstOptions();
resetExtraReg(); resetExtraReg();
resetInlineComment(); resetInlineComment();
} }
//! \cond INTERNAL
//! Grabs the current emitter state and resets the emitter state at the same time, returning the state the emitter
//! had before the state was reset.
ASMJIT_INLINE_NODEBUG State _grabState() noexcept { ASMJIT_INLINE_NODEBUG State _grabState() noexcept {
State s{_instOptions | _forcedInstOptions, _extraReg, _inlineComment}; State s{_instOptions | _forcedInstOptions, _extraReg, _inlineComment};
resetState(); resetState();
return s; return s;
} }
//! \endcond
//! \} //! \}
//! \name Sections //! \name Sections
//! \{ //! \{
//! Switches the given `section`.
//!
//! Once switched, everything is added to the given `section`.
ASMJIT_API virtual Error section(Section* section); ASMJIT_API virtual Error section(Section* section);
//! \} //! \}
@@ -634,35 +656,51 @@ public:
ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5); ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5);
//! Emits an instruction `instId` with the given `operands`. //! Emits an instruction `instId` with the given `operands`.
//!
//! This is the most universal way of emitting code, which accepts an instruction identifier and instruction
//! operands. This is called an "unchecked" API as emit doesn't provide any type checks at compile-time. This
//! allows to emit instruction with just \ref Operand instances, which could be handy in some cases - for
//! example emitting generic code where you don't know whether some operand is register, memory, or immediate.
template<typename... Args> template<typename... Args>
ASMJIT_INLINE_NODEBUG Error emit(InstId instId, Args&&... operands) { ASMJIT_INLINE_NODEBUG Error emit(InstId instId, Args&&... operands) {
return _emitI(instId, Support::ForwardOp<Args>::forward(operands)...); return _emitI(instId, Support::ForwardOp<Args>::forward(operands)...);
} }
//! Similar to \ref emit(), but uses array of `operands` instead.
ASMJIT_INLINE_NODEBUG Error emitOpArray(InstId instId, const Operand_* operands, size_t opCount) { ASMJIT_INLINE_NODEBUG Error emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
return _emitOpArray(instId, operands, opCount); return _emitOpArray(instId, operands, opCount);
} }
//! Similar to \ref emit(), but emits instruction with both instruction options and extra register, followed
//! by an array of `operands`.
ASMJIT_FORCE_INLINE Error emitInst(const BaseInst& inst, const Operand_* operands, size_t opCount) { ASMJIT_FORCE_INLINE Error emitInst(const BaseInst& inst, const Operand_* operands, size_t opCount) {
setInstOptions(inst.options()); setInstOptions(inst.options());
setExtraReg(inst.extraReg()); setExtraReg(inst.extraReg());
return _emitOpArray(inst.id(), operands, opCount); return _emitOpArray(inst.id(), operands, opCount);
} }
//! \}
//! \cond INTERNAL //! \cond INTERNAL
//! \name Emit Internals
//! \{
//! Emits an instruction - all 6 operands must be defined. //! Emits an instruction - all 6 operands must be defined.
ASMJIT_API virtual Error _emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* oExt); ASMJIT_API virtual Error _emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* oExt);
//! Emits instruction having operands stored in array. //! Emits instruction having operands stored in array.
ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount); ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount);
//! \endcond
//! \} //! \}
//! \endcond
//! \name Emit Utilities //! \name Emit Utilities
//! \{ //! \{
//! Emits a function prolog described by the given function `frame`.
ASMJIT_API Error emitProlog(const FuncFrame& frame); ASMJIT_API Error emitProlog(const FuncFrame& frame);
//! Emits a function epilog described by the given function `frame`.
ASMJIT_API Error emitEpilog(const FuncFrame& frame); ASMJIT_API Error emitEpilog(const FuncFrame& frame);
//! Emits code that reassigns function `frame` arguments to the given `args`.
ASMJIT_API Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args); ASMJIT_API Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
//! \} //! \}

View File

@@ -20,8 +20,8 @@
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
// CallConv - Init & Reset // CallConv - Initialization & Reset
// ======================= // =================================
ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept { ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept {
reset(); reset();

View File

@@ -353,8 +353,15 @@ struct CallConv {
//! Function signature. //! Function signature.
//! //!
//! Contains information about function return type, count of arguments and their TypeIds. Function signature is //! Contains information about a function return type, count of arguments, and their TypeIds. Function signature
//! a low level structure which doesn't contain platform specific or calling convention specific information. //! is a low level structure which doesn't contain platform specific or calling convention specific information.
//! It's typically used to describe function arguments in a C-API like form, which is then used to calculate a
//! \ref FuncDetail instance, which then maps function signature into a platform and calling convention specific
//! format.
//!
//! Function signature can be built either dynamically by using \ref addArg() and \ref addArgT() functionality,
//! or dynamically by using a template-based \ref FuncSignature::build() function, which maps template types
//! into a function signature.
struct FuncSignature { struct FuncSignature {
//! \name Constants //! \name Constants
//! \{ //! \{
@@ -405,6 +412,12 @@ struct FuncSignature {
_ret(ret), _ret(ret),
_args{std::forward<Args>(args)...} {} _args{std::forward<Args>(args)...} {}
//! Builds a function signature based on `RetValueAndArgs`. The first template argument is a function return type,
//! and function arguments follow.
//!
//! \note This function returns a new function signature, which can be passed to functions where it's required. It's
//! a convenience function that allows to build function signature statically based on types known at compile time,
//! which is common in JIT code generation.
template<typename... RetValueAndArgs> template<typename... RetValueAndArgs>
static ASMJIT_INLINE_NODEBUG constexpr FuncSignature build(CallConvId ccId = CallConvId::kCDecl, uint32_t vaIndex = kNoVarArgs) noexcept { static ASMJIT_INLINE_NODEBUG constexpr FuncSignature build(CallConvId ccId = CallConvId::kCDecl, uint32_t vaIndex = kNoVarArgs) noexcept {
return FuncSignature(ccId, vaIndex, (TypeId(TypeUtils::TypeIdOfT<RetValueAndArgs>::kTypeId))... ); return FuncSignature(ccId, vaIndex, (TypeId(TypeUtils::TypeIdOfT<RetValueAndArgs>::kTypeId))... );
@@ -415,16 +428,20 @@ struct FuncSignature {
//! \name Overloaded Operators //! \name Overloaded Operators
//! \{ //! \{
//! Copy assignment - function signature can be copied by value.
ASMJIT_FORCE_INLINE FuncSignature& operator=(const FuncSignature& other) noexcept = default; ASMJIT_FORCE_INLINE FuncSignature& operator=(const FuncSignature& other) noexcept = default;
//! Compares this function signature with `other` for equality..
ASMJIT_FORCE_INLINE bool operator==(const FuncSignature& other) const noexcept { return equals(other); } ASMJIT_FORCE_INLINE bool operator==(const FuncSignature& other) const noexcept { return equals(other); }
//! Compares this function signature with `other` for inequality..
ASMJIT_FORCE_INLINE bool operator!=(const FuncSignature& other) const noexcept { return !equals(other); } ASMJIT_FORCE_INLINE bool operator!=(const FuncSignature& other) const noexcept { return !equals(other); }
//! \} //! \}
//! \name Init & Reset //! \name Initialization & Reset
//! \{ //! \{
//! Resets this function signature to a default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncSignature{}; } ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncSignature{}; }
//! \} //! \}
@@ -432,6 +449,7 @@ struct FuncSignature {
//! \name Equality & Comparison //! \name Equality & Comparison
//! \{ //! \{
//! Compares this function signature with `other` for equality..
ASMJIT_INLINE_NODEBUG bool equals(const FuncSignature& other) const noexcept { ASMJIT_INLINE_NODEBUG bool equals(const FuncSignature& other) const noexcept {
return _ccId == other._ccId && return _ccId == other._ccId &&
_argCount == other._argCount && _argCount == other._argCount &&
@@ -522,7 +540,7 @@ public:
ASMJIT_DEPRECATED("Use FuncSignature instead of FuncSignatureBuilder") ASMJIT_DEPRECATED("Use FuncSignature instead of FuncSignatureBuilder")
typedef FuncSignature FuncSignatureBuilder; typedef FuncSignature FuncSignatureBuilder;
#endif // ASMJIT_NO_DEPRECATED #endif // !ASMJIT_NO_DEPRECATED
//! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address //! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address
//! (and other metadata) assigned. //! (and other metadata) assigned.
@@ -592,11 +610,13 @@ struct FuncValue {
//! //!
//! \{ //! \{
//! Assigns a register of `regType` and `regId`.
inline void assignRegData(RegType regType, uint32_t regId) noexcept { inline void assignRegData(RegType regType, uint32_t regId) noexcept {
ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0); ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0);
_data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg; _data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg;
} }
//! Assigns a stack location at `offset`.
inline void assignStackOffset(int32_t offset) noexcept { inline void assignStackOffset(int32_t offset) noexcept {
ASMJIT_ASSERT((_data & kStackOffsetMask) == 0); ASMJIT_ASSERT((_data & kStackOffsetMask) == 0);
_data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack; _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack;
@@ -610,7 +630,9 @@ struct FuncValue {
//! Returns true if the value is initialized (explicit bool cast). //! Returns true if the value is initialized (explicit bool cast).
ASMJIT_INLINE_NODEBUG explicit operator bool() const noexcept { return _data != 0; } ASMJIT_INLINE_NODEBUG explicit operator bool() const noexcept { return _data != 0; }
//! \cond INTERNAL
ASMJIT_INLINE_NODEBUG void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; } ASMJIT_INLINE_NODEBUG void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; }
//! \endcond
//! Tests whether the `FuncValue` has a flag `flag` set. //! Tests whether the `FuncValue` has a flag `flag` set.
ASMJIT_INLINE_NODEBUG bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); } ASMJIT_INLINE_NODEBUG bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); }
@@ -692,40 +714,52 @@ public:
return n; return n;
} }
//! Returns values in this value in the pack.
//!
//! \note The returned array has exactly \ref Globals::kMaxValuePack elements.
ASMJIT_INLINE_NODEBUG FuncValue* values() noexcept { return _values; } ASMJIT_INLINE_NODEBUG FuncValue* values() noexcept { return _values; }
//! \overload
ASMJIT_INLINE_NODEBUG const FuncValue* values() const noexcept { return _values; } ASMJIT_INLINE_NODEBUG const FuncValue* values() const noexcept { return _values; }
//! Resets a value at the given `index` in the pack, which makes it unassigned.
inline void resetValue(size_t index) noexcept { inline void resetValue(size_t index) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].reset(); _values[index].reset();
} }
//! Tests whether the value at the given `index` in the pack is assigned.
inline bool hasValue(size_t index) noexcept { inline bool hasValue(size_t index) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index].isInitialized(); return _values[index].isInitialized();
} }
//! Assigns a register at the given `index` to `reg` and an optional `typeId`.
inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept { inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
ASMJIT_ASSERT(reg.isPhysReg()); ASMJIT_ASSERT(reg.isPhysReg());
_values[index].initReg(reg.type(), reg.id(), typeId); _values[index].initReg(reg.type(), reg.id(), typeId);
} }
//! Assigns a register at the given `index` to `regType`, `regId`, and an optional `typeId`.
inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept { inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].initReg(regType, regId, typeId); _values[index].initReg(regType, regId, typeId);
} }
//! Assigns a stack location at the given `index` to `offset` and an optional `typeId`.
inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept { inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
_values[index].initStack(offset, typeId); _values[index].initStack(offset, typeId);
} }
//! Accesses the value in the pack at the given `index`.
//!
//! \note The maximum index value is `Globals::kMaxValuePack - 1`.
inline FuncValue& operator[](size_t index) { inline FuncValue& operator[](size_t index) {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index]; return _values[index];
} }
//! \overload
inline const FuncValue& operator[](size_t index) const { inline const FuncValue& operator[](size_t index) const {
ASMJIT_ASSERT(index < Globals::kMaxValuePack); ASMJIT_ASSERT(index < Globals::kMaxValuePack);
return _values[index]; return _values[index];
@@ -1004,87 +1038,98 @@ public:
//! \{ //! \{
//! Function attributes. //! Function attributes.
FuncAttributes _attributes; FuncAttributes _attributes {};
//! Target architecture. //! Target architecture.
Arch _arch; Arch _arch {};
//! SP register ID (to access call stack and local stack). //! SP register ID (to access call stack and local stack).
uint8_t _spRegId; uint8_t _spRegId = uint8_t(BaseReg::kIdBad);
//! SA register ID (to access stack arguments). //! SA register ID (to access stack arguments).
uint8_t _saRegId; uint8_t _saRegId = uint8_t(BaseReg::kIdBad);
//! Red zone size (copied from CallConv). //! Red zone size (copied from CallConv).
uint8_t _redZoneSize; uint8_t _redZoneSize = 0;
//! Spill zone size (copied from CallConv). //! Spill zone size (copied from CallConv).
uint8_t _spillZoneSize; uint8_t _spillZoneSize = 0;
//! Natural stack alignment (copied from CallConv). //! Natural stack alignment (copied from CallConv).
uint8_t _naturalStackAlignment; uint8_t _naturalStackAlignment = 0;
//! Minimum stack alignment to turn on dynamic alignment. //! Minimum stack alignment to turn on dynamic alignment.
uint8_t _minDynamicAlignment; uint8_t _minDynamicAlignment = 0;
//! Call stack alignment. //! Call stack alignment.
uint8_t _callStackAlignment; uint8_t _callStackAlignment = 0;
//! Local stack alignment. //! Local stack alignment.
uint8_t _localStackAlignment; uint8_t _localStackAlignment = 0;
//! Final stack alignment. //! Final stack alignment.
uint8_t _finalStackAlignment; uint8_t _finalStackAlignment = 0;
//! Adjustment of the stack before returning (X86-STDCALL). //! Adjustment of the stack before returning (X86-STDCALL).
uint16_t _calleeStackCleanup; uint16_t _calleeStackCleanup = 0;
//! Call stack size. //! Call stack size.
uint32_t _callStackSize; uint32_t _callStackSize = 0;
//! Local stack size. //! Local stack size.
uint32_t _localStackSize; uint32_t _localStackSize = 0;
//! Final stack size (sum of call stack and local stack). //! Final stack size (sum of call stack and local stack).
uint32_t _finalStackSize; uint32_t _finalStackSize = 0;
//! Local stack offset (non-zero only if call stack is used). //! Local stack offset (non-zero only if call stack is used).
uint32_t _localStackOffset; uint32_t _localStackOffset = 0;
//! Offset relative to SP that contains previous SP (before alignment). //! Offset relative to SP that contains previous SP (before alignment).
uint32_t _daOffset; uint32_t _daOffset = 0;
//! Offset of the first stack argument relative to SP. //! Offset of the first stack argument relative to SP.
uint32_t _saOffsetFromSP; uint32_t _saOffsetFromSP = 0;
//! Offset of the first stack argument relative to SA (_saRegId or FP). //! Offset of the first stack argument relative to SA (_saRegId or FP).
uint32_t _saOffsetFromSA; uint32_t _saOffsetFromSA = 0;
//! Local stack adjustment in prolog/epilog. //! Local stack adjustment in prolog/epilog.
uint32_t _stackAdjustment; uint32_t _stackAdjustment = 0;
//! Registers that are dirty. //! Registers that are dirty.
Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs; Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs {};
//! Registers that must be preserved (copied from CallConv). //! Registers that must be preserved (copied from CallConv).
Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs; Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs {};
//! Size to save/restore per register group. //! Size to save/restore per register group.
Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize; Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize {};
//! Alignment of save/restore area per register group. //! Alignment of save/restore area per register group.
Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment; Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment {};
//! Stack size required to save registers with push/pop. //! Stack size required to save registers with push/pop.
uint16_t _pushPopSaveSize; uint16_t _pushPopSaveSize = 0;
//! Stack size required to save extra registers that cannot use push/pop. //! Stack size required to save extra registers that cannot use push/pop.
uint16_t _extraRegSaveSize; uint16_t _extraRegSaveSize = 0;
//! Offset where registers saved/restored via push/pop are stored //! Offset where registers saved/restored via push/pop are stored
uint32_t _pushPopSaveOffset; uint32_t _pushPopSaveOffset = 0;
//! Offset where extra registers that cannot use push/pop are stored. //! Offset where extra registers that cannot use push/pop are stored.
uint32_t _extraRegSaveOffset; uint32_t _extraRegSaveOffset = 0;
//! \} //! \}
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
ASMJIT_INLINE_NODEBUG FuncFrame() noexcept { reset(); } //! Creates a default constructed function frame, which has initialized all members to their default values.
ASMJIT_INLINE_NODEBUG FuncFrame() noexcept = default;
//! Creates a copy of `other` function frame.
ASMJIT_INLINE_NODEBUG FuncFrame(const FuncFrame& other) noexcept = default; ASMJIT_INLINE_NODEBUG FuncFrame(const FuncFrame& other) noexcept = default;
ASMJIT_API Error init(const FuncDetail& func) noexcept; //! \}
ASMJIT_INLINE_NODEBUG void reset() noexcept { //! \name Initialization & Reset
memset(this, 0, sizeof(FuncFrame)); //! \{
_spRegId = BaseReg::kIdBad;
_saRegId = BaseReg::kIdBad; //! Initializes the function frame based on `func` detail.
_daOffset = kTagInvalidOffset; ASMJIT_API Error init(const FuncDetail& func) noexcept;
} //! Resets the function frame into its default constructed state.
ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = FuncFrame{}; }
//! \}
//! \name Overloaded Operators
//! \{
//! Copy assignment - function frame is copy assignable.
ASMJIT_INLINE_NODEBUG FuncFrame& operator=(const FuncFrame& other) noexcept = default;
//! \} //! \}
@@ -1529,8 +1574,8 @@ public:
//! Update `FuncFrame` based on function's arguments assignment. //! Update `FuncFrame` based on function's arguments assignment.
//! //!
//! \note You MUST call this in order to use `BaseEmitter::emitArgsAssignment()`, otherwise the FuncFrame would //! \note This function must be called in order to use `BaseEmitter::emitArgsAssignment()`, otherwise the \ref FuncFrame
//! not contain the information necessary to assign all arguments into the registers and/or stack specified. //! would not contain the information necessary to assign all arguments into the registers and/or stack specified.
ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept; ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept;
//! \} //! \}

View File

@@ -174,7 +174,7 @@ public:
uint32_t fillPattern = 0; uint32_t fillPattern = 0;
// Reset the content of `CreateParams`. // Reset the content of `CreateParams`.
inline void reset() noexcept { memset(this, 0, sizeof(*this)); } ASMJIT_INLINE_NODEBUG void reset() noexcept { *this = CreateParams{}; }
}; };
//! Creates a `JitAllocator` instance. //! Creates a `JitAllocator` instance.

View File

@@ -21,8 +21,8 @@ static ASMJIT_FORCE_INLINE RATiedReg* RALocal_findTiedRegByWorkId(RATiedReg* tie
return nullptr; return nullptr;
} }
// RALocalAllocator - Init & Reset // RALocalAllocator - Initialization & Reset
// =============================== // =========================================
Error RALocalAllocator::init() noexcept { Error RALocalAllocator::init() noexcept {
PhysToWorkMap* physToWorkMap; PhysToWorkMap* physToWorkMap;

View File

@@ -16,8 +16,8 @@ ASMJIT_BEGIN_NAMESPACE
// and should never be modified. // and should never be modified.
const Zone::Block Zone::_zeroBlock = { nullptr, nullptr, 0 }; const Zone::Block Zone::_zeroBlock = { nullptr, nullptr, 0 };
// Zone - Init & Reset // Zone - Initialization & Reset
// =================== // =============================
void Zone::_init(size_t blockSize, size_t blockAlignment, const Support::Temporary* temporary) noexcept { void Zone::_init(size_t blockSize, size_t blockAlignment, const Support::Temporary* temporary) noexcept {
ASMJIT_ASSERT(blockSize >= kMinBlockSize); ASMJIT_ASSERT(blockSize >= kMinBlockSize);
@@ -215,8 +215,8 @@ static bool ZoneAllocator_hasDynamicBlock(ZoneAllocator* self, ZoneAllocator::Dy
} }
#endif #endif
// ZoneAllocator - Init & Reset // ZoneAllocator - Initialization & Reset
// ============================ // ======================================
void ZoneAllocator::reset(Zone* zone) noexcept { void ZoneAllocator::reset(Zone* zone) noexcept {
// Free dynamic blocks. // Free dynamic blocks.
@@ -227,9 +227,9 @@ void ZoneAllocator::reset(Zone* zone) noexcept {
block = next; block = next;
} }
// Zero the entire class and initialize to the given `zone`.
memset(this, 0, sizeof(*this));
_zone = zone; _zone = zone;
memset(_slots, 0, sizeof(_slots));
_dynamicBlocks = nullptr;
} }
// asmjit::ZoneAllocator - Alloc & Release // asmjit::ZoneAllocator - Alloc & Release

View File

@@ -9,8 +9,8 @@
ASMJIT_BEGIN_NAMESPACE ASMJIT_BEGIN_NAMESPACE
// ZoneStackBase - Init & Reset // ZoneStackBase - Initialization & Reset
// ============================ // ======================================
Error ZoneStackBase::_init(ZoneAllocator* allocator, size_t middleIndex) noexcept { Error ZoneStackBase::_init(ZoneAllocator* allocator, size_t middleIndex) noexcept {
ZoneAllocator* oldAllocator = _allocator; ZoneAllocator* oldAllocator = _allocator;

View File

@@ -151,7 +151,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! printf("Status: %s\n", DebugUtils::errorAsString(err)); //! printf("Status: %s\n", DebugUtils::errorAsString(err));
//! //!
//! // Ambiguous operand size - the pointer requires size. //! // Ambiguous operand size - the pointer requires size.
//! err = a.inc(x86::ptr(x86::rax), 1); //! err = a.inc(x86::ptr(x86::rax));
//! printf("Status: %s\n", DebugUtils::errorAsString(err)); //! printf("Status: %s\n", DebugUtils::errorAsString(err));
//! //!
//! return 0; //! return 0;
@@ -230,6 +230,9 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! targets easily. If you want to create a register of native size dynamically by specifying its id it's also possible: //! targets easily. If you want to create a register of native size dynamically by specifying its id it's also possible:
//! //!
//! ``` //! ```
//! #include <asmjit/x86.h>
//! using namespace asmjit;
//!
//! void example(x86::Assembler& a) { //! void example(x86::Assembler& a) {
//! x86::Gp zax = a.gpz(x86::Gp::kIdAx); //! x86::Gp zax = a.gpz(x86::Gp::kIdAx);
//! x86::Gp zbx = a.gpz(x86::Gp::kIdBx); //! x86::Gp zbx = a.gpz(x86::Gp::kIdBx);
@@ -360,18 +363,19 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! x86::Gp src_a = a.zcx(); //! x86::Gp src_a = a.zcx();
//! x86::Gp src_b = a.zdx(); //! x86::Gp src_b = a.zdx();
//! //!
//! X86::Xmm vec0 = x86::xmm0; //! x86::Xmm vec0 = x86::xmm0;
//! X86::Xmm vec1 = x86::xmm1; //! x86::Xmm vec1 = x86::xmm1;
//! //!
//! // Create/initialize FuncDetail and FuncFrame. //! // Create/initialize FuncDetail and FuncFrame.
//! FuncDetail func; //! FuncDetail func;
//! func.init(FuncSignature::build<void, int*, const int*, const int*>()); //! func.init(FuncSignature::build<void, int*, const int*, const int*>(),
//! rt.environment());
//! //!
//! FuncFrame frame; //! FuncFrame frame;
//! frame.init(func); //! frame.init(func);
//! //!
//! // Make XMM0 and XMM1 dirty - RegGroup::kVec describes XMM|YMM|ZMM registers. //! // Make XMM0 and XMM1 dirty - RegGroup::kVec describes XMM|YMM|ZMM registers.
//! frame.setDirtyRegs(RegGroup::kVec, IntUtils::mask(0, 1)); //! frame.setDirtyRegs(RegGroup::kVec, Support::bitMask(0, 1));
//! //!
//! // Alternatively, if you don't want to use register masks you can pass BaseReg //! // Alternatively, if you don't want to use register masks you can pass BaseReg
//! // to addDirtyRegs(). The following code would add both xmm0 and xmm1. //! // to addDirtyRegs(). The following code would add both xmm0 and xmm1.
@@ -379,7 +383,7 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! //!
//! FuncArgsAssignment args(&func); // Create arguments assignment context. //! FuncArgsAssignment args(&func); // Create arguments assignment context.
//! args.assignAll(dst, src_a, src_b);// Assign our registers to arguments. //! args.assignAll(dst, src_a, src_b);// Assign our registers to arguments.
//! args.updateFrameInfo(frame); // Reflect our args in FuncFrame. //! args.updateFuncFrame(frame); // Reflect our args in FuncFrame.
//! frame.finalize(); // Finalize the FuncFrame (updates it). //! frame.finalize(); // Finalize the FuncFrame (updates it).
//! //!
//! a.emitProlog(frame); // Emit function prolog. //! a.emitProlog(frame); // Emit function prolog.
@@ -537,16 +541,16 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! //!
//! void prefixesExample(x86::Assembler& a) { //! void prefixesExample(x86::Assembler& a) {
//! // Lock prefix for implementing atomics: //! // Lock prefix for implementing atomics:
//! // lock add dword ptr [dst], 1 //! // lock add dword ptr [rdi], 1
//! a.lock().add(x86::dword_ptr(dst), 1); //! a.lock().add(x86::dword_ptr(x86::rdi), 1);
//! //!
//! // Similarly, XAcquire/XRelease prefixes are also available: //! // Similarly, XAcquire/XRelease prefixes are also available:
//! // xacquire add dword ptr [dst], 1 //! // xacquire add dword ptr [rdi], 1
//! a.xacquire().add(x86::dword_ptr(dst), 1); //! a.xacquire().add(x86::dword_ptr(x86::rdi), 1);
//! //!
//! // Rep prefix (see also repe/repz and repne/repnz): //! // Rep prefix (see also repe/repz and repne/repnz):
//! // rep movs byte ptr [dst], byte ptr [src] //! // rep movs byte ptr [rdi], byte ptr [rsi]
//! a.rep().movs(x86::byte_ptr(dst), x86::byte_ptr(src)); //! a.rep().movs(x86::byte_ptr(x86::rdi), x86::byte_ptr(x86::rsi));
//! //!
//! // Forcing REX prefix in 64-bit mode. //! // Forcing REX prefix in 64-bit mode.
//! // rex mov eax, 1 //! // rex mov eax, 1
@@ -610,10 +614,10 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! // ----------------- //! // -----------------
//! // //! //
//! // - Broadcast data is part of memory operand. //! // - Broadcast data is part of memory operand.
//! // - Use x86::Mem::_1toN(), which returns a new x86::Mem operand. //! // - Use x86::Mem::_1to2(), x86::Mem::_1to4(), etc..., which returns a new x86::Mem operand with broadcast.
//! //!
//! // vaddpd zmm0 {k1} {z}, zmm1, [rcx] {1to8} //! // vaddpd zmm0 {k1} {z}, zmm1, [rcx] {1to8}
//! a.k(k1).z().vaddpd(zmm0, zmm1, x86::mem(rcx)._1to8()); //! a.k(k1).z().vaddpd(zmm0, zmm1, x86::ptr(rcx)._1to8());
//! //!
//! // Embedded Rounding & Suppress-All-Exceptions //! // Embedded Rounding & Suppress-All-Exceptions
//! // ------------------------------------------- //! // -------------------------------------------

View File

@@ -247,16 +247,17 @@ int TestApp::run() {
compileTimer.stop(); compileTimer.stop();
Error err = errorHandler._err; Error err = errorHandler._err;
if (!err) { if (err == kErrorOk) {
finalizeTimer.start(); finalizeTimer.start();
err = cc->finalize(); err = cc->finalize();
finalizeTimer.stop(); finalizeTimer.stop();
} }
// The first pass is only for timing serialization and compilation, because otherwise it would be biased by // The first pass is only used for timing of serialization and compilation, because otherwise it would be
// logging, which takes much more time than finalize() does. We want to benchmark Compiler the way it would // biased by logging, which takes much more time than finalize() does. We want to benchmark Compiler the
// be used in production. // way it would be used in the production.
if (pass == 0) { if (pass == 0) {
_outputSize += code.codeSize();
compileTime += compileTimer.duration(); compileTime += compileTimer.duration();
finalizeTime += finalizeTimer.duration(); finalizeTime += finalizeTimer.duration();
continue; continue;
@@ -290,8 +291,6 @@ int TestApp::run() {
fflush(stdout); fflush(stdout);
if (err == kErrorOk) { if (err == kErrorOk) {
_outputSize += code.codeSize();
StringTmp<128> result; StringTmp<128> result;
StringTmp<128> expect; StringTmp<128> expect;