summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/src/asmjit/core/emitter.h
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/src/asmjit/core/emitter.h')
-rw-r--r--3rdparty/asmjit/src/asmjit/core/emitter.h857
1 files changed, 482 insertions, 375 deletions
diff --git a/3rdparty/asmjit/src/asmjit/core/emitter.h b/3rdparty/asmjit/src/asmjit/core/emitter.h
index cfdb6951488..09c72a68eae 100644
--- a/3rdparty/asmjit/src/asmjit/core/emitter.h
+++ b/3rdparty/asmjit/src/asmjit/core/emitter.h
@@ -1,31 +1,14 @@
-// AsmJit - Machine code generation for C++
+// This file is part of AsmJit project <https://asmjit.com>
//
-// * Official AsmJit Home Page: https://asmjit.com
-// * Official Github Repository: https://github.com/asmjit/asmjit
-//
-// Copyright (c) 2008-2020 The AsmJit Authors
-//
-// This software is provided 'as-is', without any express or implied
-// warranty. In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-// 3. This notice may not be removed or altered from any source distribution.
+// See asmjit.h or LICENSE.md for license and copyright information
+// SPDX-License-Identifier: Zlib
#ifndef ASMJIT_CORE_EMITTER_H_INCLUDED
#define ASMJIT_CORE_EMITTER_H_INCLUDED
-#include "../core/arch.h"
+#include "../core/archtraits.h"
#include "../core/codeholder.h"
+#include "../core/formatter.h"
#include "../core/inst.h"
#include "../core/operand.h"
#include "../core/type.h"
@@ -35,187 +18,286 @@ ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_core
//! \{
-// ============================================================================
-// [Forward Declarations]
-// ============================================================================
-
class ConstPool;
class FuncFrame;
class FuncArgsAssignment;
-// ============================================================================
-// [asmjit::BaseEmitter]
-// ============================================================================
+//! Align mode, used by \ref BaseEmitter::align().
+enum class AlignMode : uint8_t {
+ //! Align executable code.
+ kCode = 0,
+ //! Align non-executable code.
+ kData = 1,
+ //! Align by a sequence of zeros.
+ kZero = 2,
+
+ //! Maximum value of `AlignMode`.
+ kMaxValue = kZero
+};
-//! Provides a base foundation to emit code - specialized by `Assembler` and
-//! `BaseBuilder`.
+//! Emitter type used by \ref BaseEmitter.
+enum class EmitterType : uint8_t {
+ //! Unknown or uninitialized.
+ kNone = 0,
+ //! Emitter inherits from \ref BaseAssembler.
+ kAssembler = 1,
+ //! Emitter inherits from \ref BaseBuilder.
+ kBuilder = 2,
+ //! Emitter inherits from \ref BaseCompiler.
+ kCompiler = 3,
+
+ //! Maximum value of `EmitterType`.
+ kMaxValue = kCompiler
+};
+
+//! Emitter flags, used by \ref BaseEmitter.
+enum class EmitterFlags : uint8_t {
+ //! No flags.
+ kNone = 0u,
+ //! Emitter is attached to CodeHolder.
+ kAttached = 0x01u,
+ //! The emitter must emit comments.
+ kLogComments = 0x08u,
+ //! The emitter has its own \ref Logger (not propagated from \ref CodeHolder).
+ kOwnLogger = 0x10u,
+ //! The emitter has its own \ref ErrorHandler (not propagated from \ref CodeHolder).
+ kOwnErrorHandler = 0x20u,
+ //! The emitter was finalized.
+ kFinalized = 0x40u,
+ //! The emitter was destroyed.
+ //!
+ //! This flag is used for a very short time when an emitter is being destroyed by
+ //! CodeHolder.
+ kDestroyed = 0x80u
+};
+ASMJIT_DEFINE_ENUM_FLAGS(EmitterFlags)
+
+//! Encoding options.
+enum class EncodingOptions : uint32_t {
+ //! No encoding options.
+ kNone = 0,
+
+ //! Emit instructions that are optimized for size, if possible.
+ //!
+ //! Default: false.
+ //!
+ //! X86 Specific
+ //! ------------
+ //!
+ //! When this option is set it the assembler will try to fix instructions if possible into operation equivalent
+ //! instructions that take less bytes by taking advantage of implicit zero extension. For example instruction
+ //! like `mov r64, imm` and `and r64, imm` can be translated to `mov r32, imm` and `and r32, imm` when the
+ //! immediate constant is lesser than `2^31`.
+ kOptimizeForSize = 0x00000001u,
+
+ //! Emit optimized code-alignment sequences.
+ //!
+ //! Default: false.
+ //!
+ //! X86 Specific
+ //! ------------
+ //!
+ //! Default align sequence used by X86 architecture is one-byte (0x90) opcode that is often shown by disassemblers
+ //! as NOP. However there are more optimized align sequences for 2-11 bytes that may execute faster on certain CPUs.
+ //! If this feature is enabled AsmJit will generate specialized sequences for alignment between 2 to 11 bytes.
+ kOptimizedAlign = 0x00000002u,
+
+ //! Emit jump-prediction hints.
+ //!
+ //! Default: false.
+ //!
+ //! X86 Specific
+ //! ------------
+ //!
+ //! Jump prediction is usually based on the direction of the jump. If the jump is backward it is usually predicted as
+ //! taken; and if the jump is forward it is usually predicted as not-taken. The reason is that loops generally use
+ //! backward jumps and conditions usually use forward jumps. However this behavior can be overridden by using
+ //! instruction prefixes. If this option is enabled these hints will be emitted.
+ //!
+ //! This feature is disabled by default, because the only processor that used to take into consideration prediction
+ //! hints was P4. Newer processors implement heuristics for branch prediction and ignore static hints. This means
+ //! that this feature can be only used for annotation purposes.
+ kPredictedJumps = 0x00000010u
+};
+ASMJIT_DEFINE_ENUM_FLAGS(EncodingOptions)
+
+//! Diagnostic options are used to tell emitters and their passes to perform diagnostics when emitting or processing
+//! user code. These options control validation and extra diagnostics that can be performed by higher level emitters.
+//!
+//! Instruction Validation
+//! ----------------------
+//!
+//! \ref BaseAssembler implementation perform by default only basic checks that are necessary to identify all
+//! variations of an instruction so the correct encoding can be selected. This is fine for production-ready code
+//! as the assembler doesn't have to perform checks that would slow it down. However, sometimes these checks are
+//! beneficial especially when the project that uses AsmJit is in a development phase, in which mistakes happen
+//! often. To make the experience of using AsmJit seamless it offers validation features that can be controlled
+//! by \ref DiagnosticOptions.
+//!
+//! Compiler Diagnostics
+//! --------------------
+//!
+//! Diagnostic options work with \ref BaseCompiler passes (precisely with its register allocation pass). These options
+//! can be used to enable logging of all operations that the Compiler does.
+enum class DiagnosticOptions : uint32_t {
+ //! No validation options.
+ kNone = 0,
+
+ //! Perform strict validation in \ref BaseAssembler::emit() implementations.
+ //!
+ //! This flag ensures that each instruction is checked before it's encoded into a binary representation. This flag
+ //! is only relevant for \ref BaseAssembler implementations, but can be set in any other emitter type, in that case
+ //! if that emitter needs to create an assembler on its own, for the purpose of \ref BaseEmitter::finalize() it
+ //! would propagate this flag to such assembler so all instructions passed to it are explicitly validated.
+ //!
+ //! Default: false.
+ kValidateAssembler = 0x00000001u,
+
+ //! Perform strict validation in \ref BaseBuilder::emit() and \ref BaseCompiler::emit() implementations.
+ //!
+ //! This flag ensures that each instruction is checked before an \ref InstNode representing the instruction is
+ //! created by \ref BaseBuilder or \ref BaseCompiler. This option could be more useful than \ref kValidateAssembler
+ //! in cases in which there is an invalid instruction passed to an assembler, which was invalid much earlier, most
+ //! likely when such instruction was passed to Builder/Compiler.
+ //!
+ //! This is a separate option that was introduced, because it's possible to manipulate the instruction stream
+ //! emitted by \ref BaseBuilder and \ref BaseCompiler - this means that it's allowed to emit invalid instructions
+ //! (for example with missing operands) that will be fixed later before finalizing it.
+ //!
+ //! Default: false.
+ kValidateIntermediate = 0x00000002u,
+
+ //! Annotate all nodes processed by register allocator (Compiler/RA).
+ //!
+ //! \note Annotations don't need debug options, however, some debug options like `kRADebugLiveness` may influence
+ //! their output (for example the mentioned option would add liveness information to per-instruction annotation).
+ kRAAnnotate = 0x00000080u,
+
+ //! Debug CFG generation and other related algorithms / operations (Compiler/RA).
+ kRADebugCFG = 0x00000100u,
+
+ //! Debug liveness analysis (Compiler/RA).
+ kRADebugLiveness = 0x00000200u,
+
+ //! Debug register allocation assignment (Compiler/RA).
+ kRADebugAssignment = 0x00000400u,
+
+ //! Debug the removal of code part of unreachable blocks.
+ kRADebugUnreachable = 0x00000800u,
+
+ //! Enable all debug options (Compiler/RA).
+ kRADebugAll = 0x0000FF00u,
+};
+ASMJIT_DEFINE_ENUM_FLAGS(DiagnosticOptions)
+
+//! Provides a base foundation to emitting code - specialized by \ref BaseAssembler and \ref BaseBuilder.
class ASMJIT_VIRTAPI BaseEmitter {
public:
ASMJIT_BASE_CLASS(BaseEmitter)
+ ASMJIT_NONCOPYABLE(BaseEmitter)
+
+ //! \name Members
+ //! \{
//! See \ref EmitterType.
- uint8_t _emitterType;
- //! See \ref BaseEmitter::EmitterFlags.
- uint8_t _emitterFlags;
- //! Validation flags in case validation is used, see \ref InstAPI::ValidationFlags.
+ EmitterType _emitterType = EmitterType::kNone;
+ //! See \ref EmitterFlags.
+ EmitterFlags _emitterFlags = EmitterFlags::kNone;
+ //! Instruction alignment.
+ uint8_t _instructionAlignment = 0u;
+ //! \cond
+ uint8_t _reservedBaseEmitter = 0u;
+ //! \endcond
+ //! Validation flags in case validation is used.
//!
- //! \note Validation flags are specific to the emitter and they are setup at
- //! construction time and then never changed.
- uint8_t _validationFlags;
- //! Validation options, see \ref ValidationOptions.
- uint8_t _validationOptions;
+ //! \note Validation flags are specific to the emitter and they are setup at construction time and then never
+ //! changed.
+ ValidationFlags _validationFlags = ValidationFlags::kNone;
+ //! Validation options.
+ DiagnosticOptions _diagnosticOptions = DiagnosticOptions::kNone;
- //! Encoding options, see \ref EncodingOptions.
- uint32_t _encodingOptions;
+ //! All supported architectures in a bit-mask, where LSB is the bit with a zero index.
+ uint64_t _archMask = 0;
+
+ //! Encoding options.
+ EncodingOptions _encodingOptions = EncodingOptions::kNone;
//! Forced instruction options, combined with \ref _instOptions by \ref emit().
- uint32_t _forcedInstOptions;
+ InstOptions _forcedInstOptions = InstOptions::kReserved;
//! Internal private data used freely by any emitter.
- uint32_t _privateData;
+ uint32_t _privateData = 0;
//! CodeHolder the emitter is attached to.
- CodeHolder* _code;
+ CodeHolder* _code = nullptr;
//! Attached \ref Logger.
- Logger* _logger;
+ Logger* _logger = nullptr;
//! Attached \ref ErrorHandler.
- ErrorHandler* _errorHandler;
+ ErrorHandler* _errorHandler = nullptr;
//! Describes the target environment, matches \ref CodeHolder::environment().
- Environment _environment;
+ Environment _environment {};
//! Native GP register signature and signature related information.
- RegInfo _gpRegInfo;
+ OperandSignature _gpSignature {};
+
+ //! Emitter state that can be used to specify options and inline comment of a next node or instruction.
+ struct State {
+ InstOptions options;
+ RegOnly extraReg;
+ const char* comment;
+ };
//! Next instruction options (affects the next instruction).
- uint32_t _instOptions;
+ InstOptions _instOptions = InstOptions::kNone;
//! Extra register (op-mask {k} on AVX-512) (affects the next instruction).
- RegOnly _extraReg;
+ RegOnly _extraReg {};
//! Inline comment of the next instruction (affects the next instruction).
- const char* _inlineComment;
-
- //! Emitter type.
- enum EmitterType : uint32_t {
- //! Unknown or uninitialized.
- kTypeNone = 0,
- //! Emitter inherits from \ref BaseAssembler.
- kTypeAssembler = 1,
- //! Emitter inherits from \ref BaseBuilder.
- kTypeBuilder = 2,
- //! Emitter inherits from \ref BaseCompiler.
- kTypeCompiler = 3,
-
- //! Count of emitter types.
- kTypeCount = 4
- };
+ const char* _inlineComment = nullptr;
- //! Emitter flags.
- enum EmitterFlags : uint32_t {
- //! The emitter has its own \ref Logger (not propagated from \ref CodeHolder).
- kFlagOwnLogger = 0x10u,
- //! The emitter has its own \ref ErrorHandler (not propagated from \ref CodeHolder).
- kFlagOwnErrorHandler = 0x20u,
- //! The emitter was finalized.
- kFlagFinalized = 0x40u,
- //! The emitter was destroyed.
- kFlagDestroyed = 0x80u
+ //! Function callbacks used by emitter implementation.
+ //!
+ //! These are typically shared between Assembler/Builder/Compiler of a single backend.
+ struct Funcs {
+ typedef Error (ASMJIT_CDECL* EmitProlog)(BaseEmitter* emitter, const FuncFrame& frame);
+ typedef Error (ASMJIT_CDECL* EmitEpilog)(BaseEmitter* emitter, const FuncFrame& frame);
+ typedef Error (ASMJIT_CDECL* EmitArgsAssignment)(BaseEmitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args);
+
+ typedef Error (ASMJIT_CDECL* FormatInstruction)(
+ String& sb,
+ FormatFlags formatFlags,
+ const BaseEmitter* emitter,
+ Arch arch,
+ const BaseInst& inst, const Operand_* operands, size_t opCount) ASMJIT_NOEXCEPT_TYPE;
+
+ typedef Error (ASMJIT_CDECL* ValidateFunc)(const BaseInst& inst, const Operand_* operands, size_t opCount, ValidationFlags validationFlags) ASMJIT_NOEXCEPT_TYPE;
+
+ //! Emit prolog implementation.
+ EmitProlog emitProlog;
+ //! Emit epilog implementation.
+ EmitEpilog emitEpilog;
+ //! Emit arguments assignment implementation.
+ EmitArgsAssignment emitArgsAssignment;
+ //! Instruction formatter implementation.
+ FormatInstruction formatInstruction;
+ //! Instruction validation implementation.
+ ValidateFunc validate;
+
+ //! Resets all functions to nullptr.
+ ASMJIT_INLINE_NODEBUG void reset() noexcept {
+ emitProlog = nullptr;
+ emitEpilog = nullptr;
+ emitArgsAssignment = nullptr;
+ validate = nullptr;
+ }
};
- //! Encoding options.
- enum EncodingOptions : uint32_t {
- //! Emit instructions that are optimized for size, if possible.
- //!
- //! Default: false.
- //!
- //! X86 Specific
- //! ------------
- //!
- //! When this option is set it the assembler will try to fix instructions
- //! if possible into operation equivalent instructions that take less bytes
- //! by taking advantage of implicit zero extension. For example instruction
- //! like `mov r64, imm` and `and r64, imm` can be translated to `mov r32, imm`
- //! and `and r32, imm` when the immediate constant is lesser than `2^31`.
- kEncodingOptionOptimizeForSize = 0x00000001u,
-
- //! Emit optimized code-alignment sequences.
- //!
- //! Default: false.
- //!
- //! X86 Specific
- //! ------------
- //!
- //! Default align sequence used by X86 architecture is one-byte (0x90)
- //! opcode that is often shown by disassemblers as NOP. However there are
- //! more optimized align sequences for 2-11 bytes that may execute faster
- //! on certain CPUs. If this feature is enabled AsmJit will generate
- //! specialized sequences for alignment between 2 to 11 bytes.
- kEncodingOptionOptimizedAlign = 0x00000002u,
-
- //! Emit jump-prediction hints.
- //!
- //! Default: false.
- //!
- //! X86 Specific
- //! ------------
- //!
- //! Jump prediction is usually based on the direction of the jump. If the
- //! jump is backward it is usually predicted as taken; and if the jump is
- //! forward it is usually predicted as not-taken. The reason is that loops
- //! generally use backward jumps and conditions usually use forward jumps.
- //! However this behavior can be overridden by using instruction prefixes.
- //! If this option is enabled these hints will be emitted.
- //!
- //! This feature is disabled by default, because the only processor that
- //! used to take into consideration prediction hints was P4. Newer processors
- //! implement heuristics for branch prediction and ignore static hints. This
- //! means that this feature can be only used for annotation purposes.
- kEncodingOptionPredictedJumps = 0x00000010u
- };
+ Funcs _funcs {};
-#ifndef ASMJIT_NO_DEPRECATED
- enum EmitterOptions : uint32_t {
- kOptionOptimizedForSize = kEncodingOptionOptimizeForSize,
- kOptionOptimizedAlign = kEncodingOptionOptimizedAlign,
- kOptionPredictedJumps = kEncodingOptionPredictedJumps
- };
-#endif
-
- //! Validation options are used to tell emitters to perform strict validation
- //! of instructions passed to \ref emit().
- //!
- //! \ref BaseAssembler implementation perform by default only basic checks
- //! that are necessary to identify all variations of an instruction so the
- //! correct encoding can be selected. This is fine for production-ready code
- //! as the assembler doesn't have to perform checks that would slow it down.
- //! However, sometimes these checks are beneficial especially when the project
- //! that uses AsmJit is in a development phase, in which mistakes happen often.
- //! To make the experience of using AsmJit seamless it offers validation
- //! features that can be controlled by `ValidationOptions`.
- enum ValidationOptions : uint32_t {
- //! Perform strict validation in \ref BaseAssembler::emit() implementations.
- //!
- //! This flag ensures that each instruction is checked before it's encoded
- //! into a binary representation. This flag is only relevant for \ref
- //! BaseAssembler implementations, but can be set in any other emitter type,
- //! in that case if that emitter needs to create an assembler on its own,
- //! for the purpose of \ref finalize() it would propagate this flag to such
- //! assembler so all instructions passed to it are explicitly validated.
- //!
- //! Default: false.
- kValidationOptionAssembler = 0x00000001u,
-
- //! Perform strict validation in \ref BaseBuilder::emit() and \ref
- //! BaseCompiler::emit() implementations.
- //!
- //! This flag ensures that each instruction is checked before an \ref
- //! InstNode representing the instruction is created by Builder or Compiler.
- //!
- //! Default: false.
- kValidationOptionIntermediate = 0x00000002u
- };
+ //! \}
//! \name Construction & Destruction
//! \{
- ASMJIT_API explicit BaseEmitter(uint32_t emitterType) noexcept;
+ ASMJIT_API explicit BaseEmitter(EmitterType emitterType) noexcept;
ASMJIT_API virtual ~BaseEmitter() noexcept;
//! \}
@@ -224,10 +306,10 @@ public:
//! \{
template<typename T>
- inline T* as() noexcept { return reinterpret_cast<T*>(this); }
+ ASMJIT_INLINE_NODEBUG T* as() noexcept { return reinterpret_cast<T*>(this); }
template<typename T>
- inline const T* as() const noexcept { return reinterpret_cast<const T*>(this); }
+ ASMJIT_INLINE_NODEBUG const T* as() const noexcept { return reinterpret_cast<const T*>(this); }
//! \}
@@ -235,54 +317,69 @@ public:
//! \{
//! Returns the type of this emitter, see `EmitterType`.
- inline uint32_t emitterType() const noexcept { return _emitterType; }
+ ASMJIT_INLINE_NODEBUG EmitterType emitterType() const noexcept { return _emitterType; }
//! Returns emitter flags , see `Flags`.
- inline uint32_t emitterFlags() const noexcept { return _emitterFlags; }
+ ASMJIT_INLINE_NODEBUG EmitterFlags emitterFlags() const noexcept { return _emitterFlags; }
//! Tests whether the emitter inherits from `BaseAssembler`.
- inline bool isAssembler() const noexcept { return _emitterType == kTypeAssembler; }
+ ASMJIT_INLINE_NODEBUG bool isAssembler() const noexcept { return _emitterType == EmitterType::kAssembler; }
//! Tests whether the emitter inherits from `BaseBuilder`.
//!
//! \note Both Builder and Compiler emitters would return `true`.
- inline bool isBuilder() const noexcept { return _emitterType >= kTypeBuilder; }
+ ASMJIT_INLINE_NODEBUG bool isBuilder() const noexcept { return uint32_t(_emitterType) >= uint32_t(EmitterType::kBuilder); }
//! Tests whether the emitter inherits from `BaseCompiler`.
- inline bool isCompiler() const noexcept { return _emitterType == kTypeCompiler; }
+ ASMJIT_INLINE_NODEBUG bool isCompiler() const noexcept { return _emitterType == EmitterType::kCompiler; }
//! Tests whether the emitter has the given `flag` enabled.
- inline bool hasEmitterFlag(uint32_t flag) const noexcept { return (_emitterFlags & flag) != 0; }
+ ASMJIT_INLINE_NODEBUG bool hasEmitterFlag(EmitterFlags flag) const noexcept { return Support::test(_emitterFlags, flag); }
//! Tests whether the emitter is finalized.
- inline bool isFinalized() const noexcept { return hasEmitterFlag(kFlagFinalized); }
+ ASMJIT_INLINE_NODEBUG bool isFinalized() const noexcept { return hasEmitterFlag(EmitterFlags::kFinalized); }
//! Tests whether the emitter is destroyed (only used during destruction).
- inline bool isDestroyed() const noexcept { return hasEmitterFlag(kFlagDestroyed); }
+ ASMJIT_INLINE_NODEBUG bool isDestroyed() const noexcept { return hasEmitterFlag(EmitterFlags::kDestroyed); }
+
+ //! \}
+
+ //! \cond INTERNAL
+ //! \name Internal Functions
+ //! \{
- inline void _addEmitterFlags(uint32_t flags) noexcept { _emitterFlags = uint8_t(_emitterFlags | flags); }
- inline void _clearEmitterFlags(uint32_t flags) noexcept { _emitterFlags = uint8_t(_emitterFlags & ~flags); }
+ ASMJIT_INLINE_NODEBUG void _addEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags |= flags; }
+ ASMJIT_INLINE_NODEBUG void _clearEmitterFlags(EmitterFlags flags) noexcept { _emitterFlags &= _emitterFlags & ~flags; }
//! \}
+ //! \endcond
//! \name Target Information
//! \{
//! Returns the CodeHolder this emitter is attached to.
- inline CodeHolder* code() const noexcept { return _code; }
+ ASMJIT_INLINE_NODEBUG CodeHolder* code() const noexcept { return _code; }
- //! Returns the target environment, see \ref Environment.
+ //! Returns the target environment.
//!
//! The returned \ref Environment reference matches \ref CodeHolder::environment().
- inline const Environment& environment() const noexcept { return _environment; }
+ ASMJIT_INLINE_NODEBUG const Environment& environment() const noexcept { return _environment; }
//! Tests whether the target architecture is 32-bit.
- inline bool is32Bit() const noexcept { return environment().is32Bit(); }
+ ASMJIT_INLINE_NODEBUG bool is32Bit() const noexcept { return environment().is32Bit(); }
//! Tests whether the target architecture is 64-bit.
- inline bool is64Bit() const noexcept { return environment().is64Bit(); }
+ ASMJIT_INLINE_NODEBUG bool is64Bit() const noexcept { return environment().is64Bit(); }
//! Returns the target architecture type.
- inline uint32_t arch() const noexcept { return environment().arch(); }
+ ASMJIT_INLINE_NODEBUG Arch arch() const noexcept { return environment().arch(); }
//! Returns the target architecture sub-type.
- inline uint32_t subArch() const noexcept { return environment().subArch(); }
+ ASMJIT_INLINE_NODEBUG SubArch subArch() const noexcept { return environment().subArch(); }
//! Returns the target architecture's GP register size (4 or 8 bytes).
- inline uint32_t registerSize() const noexcept { return environment().registerSize(); }
+ ASMJIT_INLINE_NODEBUG uint32_t registerSize() const noexcept { return environment().registerSize(); }
+
+ //! Returns instruction alignment.
+ //!
+ //! The following values are returned based on the target architecture:
+ //! - X86 and X86_64 - instruction alignment is 1
+ //! - AArch32 - instruction alignment is 4 in A32 mode and 2 in THUMB mode.
+ //! - AArch64 - instruction alignment is 4
+ ASMJIT_INLINE_NODEBUG uint32_t instructionAlignment() const noexcept { return _instructionAlignment; }
//! \}
@@ -290,16 +387,14 @@ public:
//! \{
//! Tests whether the emitter is initialized (i.e. attached to \ref CodeHolder).
- inline bool isInitialized() const noexcept { return _code != nullptr; }
+ ASMJIT_INLINE_NODEBUG bool isInitialized() const noexcept { return _code != nullptr; }
//! Finalizes this emitter.
//!
- //! Materializes the content of the emitter by serializing it to the attached
- //! \ref CodeHolder through an architecture specific \ref BaseAssembler. This
- //! function won't do anything if the emitter inherits from \ref BaseAssembler
- //! as assemblers emit directly to a \ref CodeBuffer held by \ref CodeHolder.
- //! However, if this is an emitter that inherits from \ref BaseBuilder or \ref
- //! BaseCompiler then these emitters need the materialization phase as they
+ //! Materializes the content of the emitter by serializing it to the attached \ref CodeHolder through an architecture
+ //! specific \ref BaseAssembler. This function won't do anything if the emitter inherits from \ref BaseAssembler as
+ //! assemblers emit directly to a \ref CodeBuffer held by \ref CodeHolder. However, if this is an emitter that
+ //! inherits from \ref BaseBuilder or \ref BaseCompiler then these emitters need the materialization phase as they
//! store their content in a representation not visible to \ref CodeHolder.
ASMJIT_API virtual Error finalize();
@@ -309,34 +404,32 @@ public:
//! \{
//! Tests whether the emitter has a logger.
- inline bool hasLogger() const noexcept { return _logger != nullptr; }
+ ASMJIT_INLINE_NODEBUG bool hasLogger() const noexcept { return _logger != nullptr; }
//! Tests whether the emitter has its own logger.
//!
- //! Own logger means that it overrides the possible logger that may be used
- //! by \ref CodeHolder this emitter is attached to.
- inline bool hasOwnLogger() const noexcept { return hasEmitterFlag(kFlagOwnLogger); }
+ //! Own logger means that it overrides the possible logger that may be used by \ref CodeHolder this emitter is
+ //! attached to.
+ ASMJIT_INLINE_NODEBUG bool hasOwnLogger() const noexcept { return hasEmitterFlag(EmitterFlags::kOwnLogger); }
//! Returns the logger this emitter uses.
//!
- //! The returned logger is either the emitter's own logger or it's logger
- //! used by \ref CodeHolder this emitter is attached to.
- inline Logger* logger() const noexcept { return _logger; }
+ //! The returned logger is either the emitter's own logger or it's logger used by \ref CodeHolder this emitter
+ //! is attached to.
+ ASMJIT_INLINE_NODEBUG Logger* logger() const noexcept { return _logger; }
//! Sets or resets the logger of the emitter.
//!
- //! If the `logger` argument is non-null then the logger will be considered
- //! emitter's own logger, see \ref hasOwnLogger() for more details. If the
- //! given `logger` is null then the emitter will automatically use logger
+ //! If the `logger` argument is non-null then the logger will be considered emitter's own logger, see \ref
+ //! hasOwnLogger() for more details. If the given `logger` is null then the emitter will automatically use logger
//! that is attached to the \ref CodeHolder this emitter is attached to.
ASMJIT_API void setLogger(Logger* logger) noexcept;
//! Resets the logger of this emitter.
//!
- //! The emitter will bail to using a logger attached to \ref CodeHolder this
- //! emitter is attached to, or no logger at all if \ref CodeHolder doesn't
- //! have one.
- inline void resetLogger() noexcept { return setLogger(nullptr); }
+ //! The emitter will bail to using a logger attached to \ref CodeHolder this emitter is attached to, or no logger
+ //! at all if \ref CodeHolder doesn't have one.
+ ASMJIT_INLINE_NODEBUG void resetLogger() noexcept { return setLogger(nullptr); }
//! \}
@@ -344,32 +437,30 @@ public:
//! \{
//! Tests whether the emitter has an error handler attached.
- inline bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; }
+ ASMJIT_INLINE_NODEBUG bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; }
//! Tests whether the emitter has its own error handler.
//!
- //! Own error handler means that it overrides the possible error handler that
- //! may be used by \ref CodeHolder this emitter is attached to.
- inline bool hasOwnErrorHandler() const noexcept { return hasEmitterFlag(kFlagOwnErrorHandler); }
+ //! Own error handler means that it overrides the possible error handler that may be used by \ref CodeHolder this
+ //! emitter is attached to.
+ ASMJIT_INLINE_NODEBUG bool hasOwnErrorHandler() const noexcept { return hasEmitterFlag(EmitterFlags::kOwnErrorHandler); }
//! Returns the error handler this emitter uses.
//!
- //! The returned error handler is either the emitter's own error handler or
- //! it's error handler used by \ref CodeHolder this emitter is attached to.
- inline ErrorHandler* errorHandler() const noexcept { return _errorHandler; }
+ //! The returned error handler is either the emitter's own error handler or it's error handler used by
+ //! \ref CodeHolder this emitter is attached to.
+ ASMJIT_INLINE_NODEBUG ErrorHandler* errorHandler() const noexcept { return _errorHandler; }
//! Sets or resets the error handler of the emitter.
ASMJIT_API void setErrorHandler(ErrorHandler* errorHandler) noexcept;
//! Resets the error handler.
- inline void resetErrorHandler() noexcept { setErrorHandler(nullptr); }
+ ASMJIT_INLINE_NODEBUG void resetErrorHandler() noexcept { setErrorHandler(nullptr); }
//! Handles the given error in the following way:
- //! 1. If the emitter has \ref ErrorHandler attached, it calls its
- //! \ref ErrorHandler::handleError() member function first, and
- //! then returns the error. The `handleError()` function may throw.
- //! 2. if the emitter doesn't have \ref ErrorHandler, the error is
- //! simply returned.
+ //! 1. If the emitter has \ref ErrorHandler attached, it calls its \ref ErrorHandler::handleError() member function
+ //! first, and then returns the error. The `handleError()` function may throw.
+ //! 2. if the emitter doesn't have \ref ErrorHandler, the error is simply returned.
ASMJIT_API Error reportError(Error err, const char* message = nullptr);
//! \}
@@ -377,61 +468,51 @@ public:
//! \name Encoding Options
//! \{
- //! Returns encoding options, see \ref EncodingOptions.
- inline uint32_t encodingOptions() const noexcept { return _encodingOptions; }
+ //! Returns encoding options.
+ ASMJIT_INLINE_NODEBUG EncodingOptions encodingOptions() const noexcept { return _encodingOptions; }
//! Tests whether the encoding `option` is set.
- inline bool hasEncodingOption(uint32_t option) const noexcept { return (_encodingOptions & option) != 0; }
+ ASMJIT_INLINE_NODEBUG bool hasEncodingOption(EncodingOptions option) const noexcept { return Support::test(_encodingOptions, option); }
- //! Enables the given encoding `options`, see \ref EncodingOptions.
- inline void addEncodingOptions(uint32_t options) noexcept { _encodingOptions |= options; }
- //! Disables the given encoding `options`, see \ref EncodingOptions.
- inline void clearEncodingOptions(uint32_t options) noexcept { _encodingOptions &= ~options; }
+ //! Enables the given encoding `options`.
+ ASMJIT_INLINE_NODEBUG void addEncodingOptions(EncodingOptions options) noexcept { _encodingOptions |= options; }
+ //! Disables the given encoding `options`.
+ ASMJIT_INLINE_NODEBUG void clearEncodingOptions(EncodingOptions options) noexcept { _encodingOptions &= ~options; }
//! \}
- //! \name Validation Options
+ //! \name Diagnostic Options
//! \{
- //! Returns the emitter's validation options, see \ref ValidationOptions.
- inline uint32_t validationOptions() const noexcept {
- return _validationOptions;
- }
+ //! Returns the emitter's diagnostic options.
+ ASMJIT_INLINE_NODEBUG DiagnosticOptions diagnosticOptions() const noexcept { return _diagnosticOptions; }
- //! Tests whether the given `option` is present in validation options.
- inline bool hasValidationOption(uint32_t option) const noexcept {
- return (_validationOptions & option) != 0;
- }
+ //! Tests whether the given `option` is present in the emitter's diagnostic options.
+ ASMJIT_INLINE_NODEBUG bool hasDiagnosticOption(DiagnosticOptions option) const noexcept { return Support::test(_diagnosticOptions, option); }
- //! Activates the given validation `options`, see \ref ValidationOptions.
+ //! Activates the given diagnostic `options`.
//!
- //! This function is used to activate explicit validation options that will
- //! be then used by all emitter implementations. There are in general two
- //! possibilities:
+ //! This function is used to activate explicit validation options that will be then used by all emitter
+ //! implementations. There are in general two possibilities:
//!
- //! - Architecture specific assembler is used. In this case a
- //! \ref kValidationOptionAssembler can be used to turn on explicit
- //! validation that will be used before an instruction is emitted.
- //! This means that internally an extra step will be performed to
- //! make sure that the instruction is correct. This is needed, because
- //! by default assemblers prefer speed over strictness.
+ //! - Architecture specific assembler is used. In this case a \ref DiagnosticOptions::kValidateAssembler can be
+ //! used to turn on explicit validation that will be used before an instruction is emitted. This means that
+ //! internally an extra step will be performed to make sure that the instruction is correct. This is needed,
+ //! because by default assemblers prefer speed over strictness.
//!
//! This option should be used in debug builds as it's pretty expensive.
//!
- //! - Architecture specific builder or compiler is used. In this case
- //! the user can turn on \ref kValidationOptionIntermediate option
- //! that adds explicit validation step before the Builder or Compiler
- //! creates an \ref InstNode to represent an emitted instruction. Error
- //! will be returned if the instruction is ill-formed. In addition,
- //! also \ref kValidationOptionAssembler can be used, which would not be
- //! consumed by Builder / Compiler directly, but it would be propagated
- //! to an architecture specific \ref BaseAssembler implementation it
- //! creates during \ref BaseEmitter::finalize().
- ASMJIT_API void addValidationOptions(uint32_t options) noexcept;
+ //! - Architecture specific builder or compiler is used. In this case the user can turn on
+ //! \ref DiagnosticOptions::kValidateIntermediate option that adds explicit validation step before the Builder
+ //! or Compiler creates an \ref InstNode to represent an emitted instruction. Error will be returned if the
+ //! instruction is ill-formed. In addition, also \ref DiagnosticOptions::kValidateAssembler can be used, which
+ //! would not be consumed by Builder / Compiler directly, but it would be propagated to an architecture specific
+ //! \ref BaseAssembler implementation it creates during \ref BaseEmitter::finalize().
+ ASMJIT_API void addDiagnosticOptions(DiagnosticOptions options) noexcept;
//! Deactivates the given validation `options`.
//!
- //! See \ref addValidationOptions() and \ref ValidationOptions for more details.
- ASMJIT_API void clearValidationOptions(uint32_t options) noexcept;
+ //! See \ref addDiagnosticOptions() and \ref DiagnosticOptions for more details.
+ ASMJIT_API void clearDiagnosticOptions(DiagnosticOptions options) noexcept;
//! \}
@@ -440,49 +521,79 @@ public:
//! Returns forced instruction options.
//!
- //! Forced instruction options are merged with next instruction options before
- //! the instruction is encoded. These options have some bits reserved that are
- //! used by error handling, logging, and instruction validation purposes. Other
- //! options are globals that affect each instruction.
- inline uint32_t forcedInstOptions() const noexcept { return _forcedInstOptions; }
+ //! Forced instruction options are merged with next instruction options before the instruction is encoded. These
+ //! options have some bits reserved that are used by error handling, logging, and instruction validation purposes.
+ //! Other options are globals that affect each instruction.
+ ASMJIT_INLINE_NODEBUG InstOptions forcedInstOptions() const noexcept { return _forcedInstOptions; }
//! Returns options of the next instruction.
- inline uint32_t instOptions() const noexcept { return _instOptions; }
+ ASMJIT_INLINE_NODEBUG InstOptions instOptions() const noexcept { return _instOptions; }
//! Returns options of the next instruction.
- inline void setInstOptions(uint32_t options) noexcept { _instOptions = options; }
+ ASMJIT_INLINE_NODEBUG void setInstOptions(InstOptions options) noexcept { _instOptions = options; }
//! Adds options of the next instruction.
- inline void addInstOptions(uint32_t options) noexcept { _instOptions |= options; }
+ ASMJIT_INLINE_NODEBUG void addInstOptions(InstOptions options) noexcept { _instOptions |= options; }
//! Resets options of the next instruction.
- inline void resetInstOptions() noexcept { _instOptions = 0; }
+ ASMJIT_INLINE_NODEBUG void resetInstOptions() noexcept { _instOptions = InstOptions::kNone; }
//! Tests whether the extra register operand is valid.
- inline bool hasExtraReg() const noexcept { return _extraReg.isReg(); }
+ ASMJIT_INLINE_NODEBUG bool hasExtraReg() const noexcept { return _extraReg.isReg(); }
//! Returns an extra operand that will be used by the next instruction (architecture specific).
- inline const RegOnly& extraReg() const noexcept { return _extraReg; }
+ ASMJIT_INLINE_NODEBUG const RegOnly& extraReg() const noexcept { return _extraReg; }
//! Sets an extra operand that will be used by the next instruction (architecture specific).
- inline void setExtraReg(const BaseReg& reg) noexcept { _extraReg.init(reg); }
+ ASMJIT_INLINE_NODEBUG void setExtraReg(const BaseReg& reg) noexcept { _extraReg.init(reg); }
//! Sets an extra operand that will be used by the next instruction (architecture specific).
- inline void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); }
+ ASMJIT_INLINE_NODEBUG void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); }
//! Resets an extra operand that will be used by the next instruction (architecture specific).
- inline void resetExtraReg() noexcept { _extraReg.reset(); }
+ ASMJIT_INLINE_NODEBUG void resetExtraReg() noexcept { _extraReg.reset(); }
//! Returns comment/annotation of the next instruction.
- inline const char* inlineComment() const noexcept { return _inlineComment; }
+ ASMJIT_INLINE_NODEBUG const char* inlineComment() const noexcept { return _inlineComment; }
//! Sets comment/annotation of the next instruction.
//!
- //! \note This string is set back to null by `_emit()`, but until that it has
- //! to remain valid as the Emitter is not required to make a copy of it (and
- //! it would be slow to do that for each instruction).
- inline void setInlineComment(const char* s) noexcept { _inlineComment = s; }
+ //! \note This string is set back to null by `_emit()`, but until that it has to remain valid as the Emitter is not
+ //! required to make a copy of it (and it would be slow to do that for each instruction).
+ ASMJIT_INLINE_NODEBUG void setInlineComment(const char* s) noexcept { _inlineComment = s; }
//! Resets the comment/annotation to nullptr.
- inline void resetInlineComment() noexcept { _inlineComment = nullptr; }
+ ASMJIT_INLINE_NODEBUG void resetInlineComment() noexcept { _inlineComment = nullptr; }
+
+ //! \}
+
+ //! \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 {
+ resetInstOptions();
+ resetExtraReg();
+ 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 {
+ State s{_instOptions | _forcedInstOptions, _extraReg, _inlineComment};
+ resetState();
+ return s;
+ }
+ //! \endcond
//! \}
//! \name Sections
//! \{
- virtual Error section(Section* section) = 0;
+ //! Switches the given `section`.
+ //!
+ //! Once switched, everything is added to the given `section`.
+ ASMJIT_API virtual Error section(Section* section);
//! \}
@@ -490,87 +601,106 @@ public:
//! \{
//! Creates a new label.
- virtual Label newLabel() = 0;
+ ASMJIT_API virtual Label newLabel();
//! Creates a new named label.
- virtual Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) = 0;
+ ASMJIT_API virtual Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, LabelType type = LabelType::kGlobal, uint32_t parentId = Globals::kInvalidId);
+
+ //! Creates a new anonymous label with a name, which can only be used for debugging purposes.
+ ASMJIT_INLINE_NODEBUG Label newAnonymousLabel(const char* name, size_t nameSize = SIZE_MAX) { return newNamedLabel(name, nameSize, LabelType::kAnonymous); }
+ //! Creates a new external label.
+ ASMJIT_INLINE_NODEBUG Label newExternalLabel(const char* name, size_t nameSize = SIZE_MAX) { return newNamedLabel(name, nameSize, LabelType::kExternal); }
//! Returns `Label` by `name`.
//!
//! Returns invalid Label in case that the name is invalid or label was not found.
//!
- //! \note This function doesn't trigger ErrorHandler in case the name is invalid
- //! or no such label exist. You must always check the validity of the `Label` returned.
+ //! \note This function doesn't trigger ErrorHandler in case the name is invalid or no such label exist. You must
+ //! always check the validity of the `Label` returned.
ASMJIT_API Label labelByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept;
//! Binds the `label` to the current position of the current section.
//!
//! \note Attempt to bind the same label multiple times will return an error.
- virtual Error bind(const Label& label) = 0;
+ ASMJIT_API virtual Error bind(const Label& label);
//! Tests whether the label `id` is valid (i.e. registered).
ASMJIT_API bool isLabelValid(uint32_t labelId) const noexcept;
//! Tests whether the `label` is valid (i.e. registered).
- inline bool isLabelValid(const Label& label) const noexcept { return isLabelValid(label.id()); }
+ ASMJIT_INLINE_NODEBUG bool isLabelValid(const Label& label) const noexcept { return isLabelValid(label.id()); }
//! \}
//! \name Emit
//! \{
- // NOTE: These `emit()` helpers are designed to address a code-bloat generated
- // by C++ compilers to call a function having many arguments. Each parameter to
- // `_emit()` requires some code to pass it, which means that if we default to
- // 5 arguments in `_emit()` and instId the C++ compiler would have to generate
- // a virtual function call having 5 parameters and additional `this` argument,
- // which is quite a lot. Since by default most instructions have 2 to 3 operands
- // it's better to introduce helpers that pass from 0 to 6 operands that help to
- // reduce the size of emit(...) function call.
+ // NOTE: These `emit()` helpers are designed to address a code-bloat generated by C++ compilers to call a function
+ // having many arguments. Each parameter to `_emit()` requires some code to pass it, which means that if we default
+ // to 5 arguments in `_emit()` and instId the C++ compiler would have to generate a virtual function call having 5
+ // parameters and additional `this` argument, which is quite a lot. Since by default most instructions have 2 to 3
+ // operands it's better to introduce helpers that pass from 0 to 6 operands that help to reduce the size of emit(...)
+ // function call.
//! Emits an instruction (internal).
- ASMJIT_API Error _emitI(uint32_t instId);
+ ASMJIT_API Error _emitI(InstId instId);
//! \overload
- ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0);
+ ASMJIT_API Error _emitI(InstId instId, const Operand_& o0);
//! \overload
- ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1);
+ ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1);
//! \overload
- ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2);
+ ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2);
//! \overload
- ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
+ ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
//! \overload
- ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4);
+ ASMJIT_API Error _emitI(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4);
//! \overload
- ASMJIT_API Error _emitI(uint32_t 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`.
+ //!
+ //! 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>
- ASMJIT_INLINE Error emit(uint32_t instId, Args&&... operands) {
+ ASMJIT_INLINE_NODEBUG Error emit(InstId instId, Args&&... operands) {
return _emitI(instId, Support::ForwardOp<Args>::forward(operands)...);
}
- inline Error emitOpArray(uint32_t instId, const Operand_* operands, size_t opCount) {
+ //! Similar to \ref emit(), but uses array of `operands` instead.
+ ASMJIT_INLINE_NODEBUG Error emitOpArray(InstId instId, const Operand_* operands, size_t opCount) {
return _emitOpArray(instId, operands, opCount);
}
- inline Error emitInst(const BaseInst& inst, const Operand_* operands, size_t 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) {
setInstOptions(inst.options());
setExtraReg(inst.extraReg());
return _emitOpArray(inst.id(), operands, opCount);
}
+ //! \}
+
//! \cond INTERNAL
+ //! \name Emit Internals
+ //! \{
+
//! Emits an instruction - all 6 operands must be defined.
- virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* oExt) = 0;
+ 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.
- virtual Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t opCount);
- //! \endcond
+ ASMJIT_API virtual Error _emitOpArray(InstId instId, const Operand_* operands, size_t opCount);
//! \}
+ //! \endcond
//! \name Emit Utilities
//! \{
+ //! Emits a function prolog described by the given function `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);
+ //! Emits code that reassigns function `frame` arguments to the given `args`.
ASMJIT_API Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
//! \}
@@ -580,9 +710,10 @@ public:
//! Aligns the current CodeBuffer position to the `alignment` specified.
//!
- //! The sequence that is used to fill the gap between the aligned location
- //! and the current location depends on the align `mode`, see \ref AlignMode.
- virtual Error align(uint32_t alignMode, uint32_t alignment) = 0;
+ //! The sequence that is used to fill the gap between the aligned location and the current location depends on the
+ //! align `mode`, see \ref AlignMode. The `alignment` argument specifies alignment in bytes, so for example when
+ //! it's `32` it means that the code buffer will be aligned to `32` bytes.
+ ASMJIT_API virtual Error align(AlignMode alignMode, uint32_t alignment);
//! \}
@@ -590,51 +721,55 @@ public:
//! \{
//! Embeds raw data into the \ref CodeBuffer.
- virtual Error embed(const void* data, size_t dataSize) = 0;
+ ASMJIT_API virtual Error embed(const void* data, size_t dataSize);
//! Embeds a typed data array.
//!
//! This is the most flexible function for embedding data as it allows to:
- //! - Assign a `typeId` to the data, so the emitter knows the type of
- //! items stored in `data`. Binary data should use \ref Type::kIdU8.
- //! - Repeat the given data `repeatCount` times, so the data can be used
- //! as a fill pattern for example, or as a pattern used by SIMD instructions.
- virtual Error embedDataArray(uint32_t typeId, const void* data, size_t itemCount, size_t repeatCount = 1) = 0;
+ //!
+ //! - Assign a `typeId` to the data, so the emitter knows the type of items stored in `data`. Binary data should
+ //! use \ref TypeId::kUInt8.
+ //!
+ //! - Repeat the given data `repeatCount` times, so the data can be used as a fill pattern for example, or as a
+ //! pattern used by SIMD instructions.
+ ASMJIT_API virtual Error embedDataArray(TypeId typeId, const void* data, size_t itemCount, size_t repeatCount = 1);
//! Embeds int8_t `value` repeated by `repeatCount`.
- inline Error embedInt8(int8_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI8, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedInt8(int8_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt8, &value, 1, repeatCount); }
//! Embeds uint8_t `value` repeated by `repeatCount`.
- inline Error embedUInt8(uint8_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU8, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedUInt8(uint8_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt8, &value, 1, repeatCount); }
//! Embeds int16_t `value` repeated by `repeatCount`.
- inline Error embedInt16(int16_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI16, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedInt16(int16_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt16, &value, 1, repeatCount); }
//! Embeds uint16_t `value` repeated by `repeatCount`.
- inline Error embedUInt16(uint16_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU16, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedUInt16(uint16_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt16, &value, 1, repeatCount); }
//! Embeds int32_t `value` repeated by `repeatCount`.
- inline Error embedInt32(int32_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI32, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedInt32(int32_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt32, &value, 1, repeatCount); }
//! Embeds uint32_t `value` repeated by `repeatCount`.
- inline Error embedUInt32(uint32_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU32, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedUInt32(uint32_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt32, &value, 1, repeatCount); }
//! Embeds int64_t `value` repeated by `repeatCount`.
- inline Error embedInt64(int64_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI64, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedInt64(int64_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kInt64, &value, 1, repeatCount); }
//! Embeds uint64_t `value` repeated by `repeatCount`.
- inline Error embedUInt64(uint64_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU64, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedUInt64(uint64_t value, size_t repeatCount = 1) { return embedDataArray(TypeId::kUInt64, &value, 1, repeatCount); }
//! Embeds a floating point `value` repeated by `repeatCount`.
- inline Error embedFloat(float value, size_t repeatCount = 1) { return embedDataArray(Type::kIdF32, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedFloat(float value, size_t repeatCount = 1) { return embedDataArray(TypeId(TypeUtils::TypeIdOfT<float>::kTypeId), &value, 1, repeatCount); }
//! Embeds a floating point `value` repeated by `repeatCount`.
- inline Error embedDouble(double value, size_t repeatCount = 1) { return embedDataArray(Type::IdOfT<double>::kTypeId, &value, 1, repeatCount); }
+ ASMJIT_INLINE_NODEBUG Error embedDouble(double value, size_t repeatCount = 1) { return embedDataArray(TypeId(TypeUtils::TypeIdOfT<double>::kTypeId), &value, 1, repeatCount); }
//! Embeds a constant pool at the current offset by performing the following:
- //! 1. Aligns by using kAlignData to the minimum `pool` alignment.
+ //! 1. Aligns by using AlignMode::kData to the minimum `pool` alignment.
//! 2. Binds the ConstPool label so it's bound to an aligned location.
//! 3. Emits ConstPool content.
- virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0;
+ ASMJIT_API virtual Error embedConstPool(const Label& label, const ConstPool& pool);
- //! Embeds an absolute label address as data (4 or 8 bytes).
- virtual Error embedLabel(const Label& label) = 0;
+ //! Embeds an absolute `label` address as data.
+ //!
+ //! The `dataSize` is an optional argument that can be used to specify the size of the address data. If it's zero
+ //! (default) the address size is deduced from the target architecture (either 4 or 8 bytes).
+ ASMJIT_API virtual Error embedLabel(const Label& label, size_t dataSize = 0);
- //! Embeds a delta (distance) between the `label` and `base` calculating it
- //! as `label - base`. This function was designed to make it easier to embed
- //! lookup tables where each index is a relative distance of two labels.
- virtual Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize) = 0;
+ //! Embeds a delta (distance) between the `label` and `base` calculating it as `label - base`. This function was
+ //! designed to make it easier to embed lookup tables where each index is a relative distance of two labels.
+ ASMJIT_API virtual Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize = 0);
//! \}
@@ -642,7 +777,7 @@ public:
//! \{
//! Emits a comment stored in `data` with an optional `size` parameter.
- virtual Error comment(const char* data, size_t size = SIZE_MAX) = 0;
+ ASMJIT_API virtual Error comment(const char* data, size_t size = SIZE_MAX);
//! Emits a formatted comment specified by `fmt` and variable number of arguments.
ASMJIT_API Error commentf(const char* fmt, ...);
@@ -655,52 +790,24 @@ public:
//! \{
//! Called after the emitter was attached to `CodeHolder`.
- virtual Error onAttach(CodeHolder* code) noexcept = 0;
+ ASMJIT_API virtual Error onAttach(CodeHolder* ASMJIT_NONNULL(code)) noexcept;
//! Called after the emitter was detached from `CodeHolder`.
- virtual Error onDetach(CodeHolder* code) noexcept = 0;
+ ASMJIT_API virtual Error onDetach(CodeHolder* ASMJIT_NONNULL(code)) noexcept;
- //! Called when \ref CodeHolder has updated an important setting, which
- //! involves the following:
+ //! Called when \ref CodeHolder has updated an important setting, which involves the following:
//!
- //! - \ref Logger has been changed (\ref CodeHolder::setLogger() has been
- //! called).
- //! - \ref ErrorHandler has been changed (\ref CodeHolder::setErrorHandler()
- //! has been called).
+ //! - \ref Logger has been changed (\ref CodeHolder::setLogger() has been called).
//!
- //! This function ensures that the settings are properly propagated from
- //! \ref CodeHolder to the emitter.
+ //! - \ref ErrorHandler has been changed (\ref CodeHolder::setErrorHandler() has been called).
//!
- //! \note This function is virtual and can be overridden, however, if you
- //! do so, always call \ref BaseEmitter::onSettingsUpdated() within your
- //! own implementation to ensure that the emitter is in a consisten state.
+ //! This function ensures that the settings are properly propagated from \ref CodeHolder to the emitter.
+ //!
+ //! \note This function is virtual and can be overridden, however, if you do so, always call \ref
+ //! BaseEmitter::onSettingsUpdated() within your own implementation to ensure that the emitter is
+ //! in a consistent state.
ASMJIT_API virtual void onSettingsUpdated() noexcept;
//! \}
-
-#ifndef ASMJIT_NO_DEPRECATED
- ASMJIT_DEPRECATED("Use environment() instead")
- inline CodeInfo codeInfo() const noexcept {
- return CodeInfo(_environment, _code ? _code->baseAddress() : Globals::kNoBaseAddress);
- }
-
- ASMJIT_DEPRECATED("Use arch() instead")
- inline uint32_t archId() const noexcept { return arch(); }
-
- ASMJIT_DEPRECATED("Use registerSize() instead")
- inline uint32_t gpSize() const noexcept { return registerSize(); }
-
- ASMJIT_DEPRECATED("Use encodingOptions() instead")
- inline uint32_t emitterOptions() const noexcept { return encodingOptions(); }
-
- ASMJIT_DEPRECATED("Use addEncodingOptions() instead")
- inline void addEmitterOptions(uint32_t options) noexcept { addEncodingOptions(options); }
-
- ASMJIT_DEPRECATED("Use clearEncodingOptions() instead")
- inline void clearEmitterOptions(uint32_t options) noexcept { clearEncodingOptions(options); }
-
- ASMJIT_DEPRECATED("Use forcedInstOptions() instead")
- inline uint32_t globalInstOptions() const noexcept { return forcedInstOptions(); }
-#endif // !ASMJIT_NO_DEPRECATED
};
//! \}