foxygit / GZDoom Log in
commits tags

/libraries/asmjit/asmjit/base/codeemitter.h · 23.21 KB

raw
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.

// [Guard]
#ifndef _ASMJIT_BASE_CODEEMITTER_H
#define _ASMJIT_BASE_CODEEMITTER_H

// [Dependencies]
#include "../base/arch.h"
#include "../base/codeholder.h"
#include "../base/operand.h"

// [Api-Begin]
#include "../asmjit_apibegin.h"

namespace asmjit {

//! \addtogroup asmjit_base
//! \{

// ============================================================================
// [Forward Declarations]
// ============================================================================

class ConstPool;

// ============================================================================
// [asmjit::CodeEmitter]
// ============================================================================

//! Provides a base foundation to emit code - specialized by \ref Assembler and
//! \ref CodeBuilder.
class ASMJIT_VIRTAPI CodeEmitter {
public:
  //! CodeEmitter type.
  ASMJIT_ENUM(Type) {
    kTypeNone       = 0,
    kTypeAssembler  = 1,
    kTypeBuilder    = 2,
    kTypeCompiler   = 3,
    kTypeCount      = 4
  };

  //! CodeEmitter hints - global settings that affect machine-code generation.
  ASMJIT_ENUM(Hints) {
    //! Emit optimized code-alignment sequences.
    //!
    //! Default `true`.
    //!
    //! X86/X64 Specific
    //! ----------------
    //!
    //! Default align sequence used by X86/X64 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.
    //! If this feature is enabled AsmJit will generate specialized sequences
    //! for alignment between 2 to 11 bytes.
    kHintOptimizedAlign = 0x00000001U,

    //! Emit jump-prediction hints.
    //!
    //! Default `false`.
    //!
    //! X86/X64 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 that ignores any static hints.
    kHintPredictedJumps = 0x00000002U
  };

  //! CodeEmitter options that are merged with instruction options.
  ASMJIT_ENUM(Options) {
    //! Reserved, used to check for errors in `Assembler::_emit()`. In addition,
    //! if an emitter is in error state it will have `kOptionMaybeFailureCase`
    //! set
    kOptionMaybeFailureCase = 0x00000001U,

    //! Perform a strict validation before the instruction is emitted.
    kOptionStrictValidation = 0x00000002U,

    //! Logging is enabled and `CodeHolder::getLogger()` should return a valid
    //! \ref Logger pointer.
    kOptionLoggingEnabled   = 0x00000004U,

    //! Mask of all internal options that are not used to represent instruction
    //! options, but are used to instrument Assembler and CodeBuilder. These
    //! options are internal and should not be used outside of AsmJit itself.
    //!
    //! NOTE: Reserved options should never appear in `CBInst` options.
    kOptionReservedMask = 0x00000007U,

    //! Used only by Assembler to mark `_op4` and `_op5` are used.
    kOptionOp4Op5Used = 0x00000008U,

    //! Prevents following a jump during compilation (CodeCompiler).
    kOptionUnfollow = 0x00000010U,

    //! Overwrite the destination operand (CodeCompiler).
    //!
    //! Hint that is important for register liveness analysis. It tells the
    //! compiler that the destination operand will be overwritten now or by
    //! adjacent instructions. CodeCompiler knows when a register is completely
    //! overwritten by a single instruction, for example you don't have to
    //! mark "movaps" or "pxor x, x", however, if a pair of instructions is
    //! used and the first of them doesn't completely overwrite the content
    //! of the destination, CodeCompiler fails to mark that register as dead.
    //!
    //! X86/X64 Specific
    //! ----------------
    //!
    //!   - All instructions that always overwrite at least the size of the
    //!     register the virtual-register uses , for example "mov", "movq",
    //!     "movaps" don't need the overwrite option to be used - conversion,
    //!     shuffle, and other miscellaneous instructions included.
    //!
    //!   - All instructions that clear the destination register if all operands
    //!     are the same, for example "xor x, x", "pcmpeqb x x", etc...
    //!
    //!   - Consecutive instructions that partially overwrite the variable until
    //!     there is no old content require the `overwrite()` to be used. Some
    //!     examples (not always the best use cases thought):
    //!
    //!     - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa
    //!     - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa
    //!     - `mov al, ?` followed by `and ax, 0xFF`
    //!     - `mov al, ?` followed by `mov ah, al`
    //!     - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1`
    //!
    //!   - If allocated variable is used temporarily for scalar operations. For
    //!     example if you allocate a full vector like `X86Compiler::newXmm()`
    //!     and then use that vector for scalar operations you should use
    //!     `overwrite()` directive:
    //!
    //!     - `sqrtss x, y` - only LO element of `x` is changed, if you don't use
    //!       HI elements, use `X86Compiler.overwrite().sqrtss(x, y)`.
    kOptionOverwrite = 0x00000020U
  };

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  ASMJIT_API CodeEmitter(uint32_t type) noexcept;
  ASMJIT_API virtual ~CodeEmitter() noexcept;

  // --------------------------------------------------------------------------
  // [Events]
  // --------------------------------------------------------------------------

  //! Called after the \ref CodeEmitter was attached to the \ref CodeHolder.
  virtual Error onAttach(CodeHolder* code) noexcept = 0;
  //! Called after the \ref CodeEmitter was detached from the \ref CodeHolder.
  virtual Error onDetach(CodeHolder* code) noexcept = 0;

  // --------------------------------------------------------------------------
  // [Code-Generation]
  // --------------------------------------------------------------------------

  //! Emit instruction having max 4 operands.
  virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0;
  //! Emit instruction having max 6 operands.
  virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) = 0;
  //! Emit instruction having operands stored in array.
  virtual Error _emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount);

  //! Create a new label.
  virtual Label newLabel() = 0;
  //! Create a new named label.
  virtual Label newNamedLabel(
    const char* name,
    size_t nameLength = Globals::kInvalidIndex,
    uint32_t type = Label::kTypeGlobal,
    uint32_t parentId = 0) = 0;

  //! Get a 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
  //! \ref Label returned.
  ASMJIT_API Label getLabelByName(
    const char* name,
    size_t nameLength = Globals::kInvalidIndex,
    uint32_t parentId = 0) noexcept;

  //! Bind 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;

  //! Align 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 mode, uint32_t alignment) = 0;

  //! Embed raw data into the code-buffer.
  virtual Error embed(const void* data, uint32_t size) = 0;

  //! Embed absolute label address as data (4 or 8 bytes).
  virtual Error embedLabel(const Label& label) = 0;

  //! Embed a constant pool into the code-buffer in the following steps:
  //!   1. Align by using kAlignData to the minimum `pool` alignment.
  //!   2. Bind `label` so it's bound to an aligned location.
  //!   3. Emit constant pool data.
  virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0;

  //! Emit a comment string `s` with an optional `len` parameter.
  virtual Error comment(const char* s, size_t len = Globals::kInvalidIndex) = 0;

  // --------------------------------------------------------------------------
  // [Code-Generation Status]
  // --------------------------------------------------------------------------

  //! Get if the CodeEmitter is initialized (i.e. attached to a \ref CodeHolder).
  ASMJIT_INLINE bool isInitialized() const noexcept { return _code != nullptr; }

  ASMJIT_API virtual Error finalize();

  // --------------------------------------------------------------------------
  // [Code Information]
  // --------------------------------------------------------------------------

  //! Get information about the code, see \ref CodeInfo.
  ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; }
  //! Get \ref CodeHolder this CodeEmitter is attached to.
  ASMJIT_INLINE CodeHolder* getCode() const noexcept { return _code; }

  //! Get information about the architecture, see \ref ArchInfo.
  ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); }

  //! Get if the target architecture is 32-bit.
  ASMJIT_INLINE bool is32Bit() const noexcept { return getArchInfo().is32Bit(); }
  //! Get if the target architecture is 64-bit.
  ASMJIT_INLINE bool is64Bit() const noexcept { return getArchInfo().is64Bit(); }

  //! Get the target architecture type.
  ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); }
  //! Get the target architecture sub-type.
  ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); }
  //! Get the target architecture's GP register size (4 or 8 bytes).
  ASMJIT_INLINE uint32_t getGpSize() const noexcept { return getArchInfo().getGpSize(); }
  //! Get the number of target GP registers.
  ASMJIT_INLINE uint32_t getGpCount() const noexcept { return getArchInfo().getGpCount(); }

  // --------------------------------------------------------------------------
  // [Code-Emitter Type]
  // --------------------------------------------------------------------------

  //! Get the type of this CodeEmitter, see \ref Type.
  ASMJIT_INLINE uint32_t getType() const noexcept { return _type; }

  ASMJIT_INLINE bool isAssembler() const noexcept { return _type == kTypeAssembler; }
  ASMJIT_INLINE bool isCodeBuilder() const noexcept { return _type == kTypeBuilder; }
  ASMJIT_INLINE bool isCodeCompiler() const noexcept { return _type == kTypeCompiler; }

  // --------------------------------------------------------------------------
  // [Global Information]
  // --------------------------------------------------------------------------

  //! Get global hints.
  ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; }

  //! Get global options.
  //!
  //! Global options are merged with instruction options before the instruction
  //! is encoded. These options have some bits reserved that are used for error
  //! checking, logging, and strict validation. Other options are globals that
  //! affect each instruction, for example if VEX3 is set globally, it will all
  //! instructions, even those that don't have such option set.
  ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; }

  // --------------------------------------------------------------------------
  // [Error Handling]
  // --------------------------------------------------------------------------

  //! Get if the object is in error state.
  //!
  //! Error state means that it does not consume anything unless the error
  //! state is reset by calling `resetLastError()`. Use `getLastError()` to
  //! get the last error that put the object into the error state.
  ASMJIT_INLINE bool isInErrorState() const noexcept { return _lastError != kErrorOk; }

  //! Get the last error code.
  ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; }
  //! Set the last error code and propagate it through the error handler.
  ASMJIT_API Error setLastError(Error error, const char* message = nullptr);
  //! Clear the last error code and return `kErrorOk`.
  ASMJIT_INLINE Error resetLastError() noexcept { return setLastError(kErrorOk); }

  // --------------------------------------------------------------------------
  // [Accessors That Affect the Next Instruction]
  // --------------------------------------------------------------------------

  //! Get options of the next instruction.
  ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; }
  //! Set options of the next instruction.
  ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; }
  //! Add options of the next instruction.
  ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; }
  //! Reset options of the next instruction.
  ASMJIT_INLINE void resetOptions() noexcept { _options = 0; }

  //! Get if the extra register operand is valid.
  ASMJIT_INLINE bool hasExtraReg() const noexcept { return _extraReg.isValid(); }
  //! Get an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _extraReg; }
  //! Set an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE void setExtraReg(const Reg& reg) noexcept { _extraReg.init(reg); }
  //! Set an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); }
  //! Reset an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE void resetExtraReg() noexcept { _extraReg.reset(); }

  //! Get annotation of the next instruction.
  ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; }
  //! Set annotation of the next instruction.
  //!
  //! NOTE: This string is set back to null by `_emit()`, but until that it has
  //! to remain valid as `CodeEmitter` is not required to make a copy of it (and
  //! it would be slow to do that for each instruction).
  ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; }
  //! Reset annotation of the next instruction to null.
  ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; }

  // --------------------------------------------------------------------------
  // [Helpers]
  // --------------------------------------------------------------------------

  //! Get if the `label` is valid (i.e. registered).
  ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept {
    return isLabelValid(label.getId());
  }

  //! Get if the label `id` is valid (i.e. registered).
  ASMJIT_API bool isLabelValid(uint32_t id) const noexcept;

  //! Emit a formatted string `fmt`.
  ASMJIT_API Error commentf(const char* fmt, ...);
  //! Emit a formatted string `fmt` (va_list version).
  ASMJIT_API Error commentv(const char* fmt, va_list ap);

  // --------------------------------------------------------------------------
  // [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 code to pass it, which means that if we default to 4
  // operand parameters in `_emit()` and instId the C++ compiler would have to
  // generate a virtual function call having 5 parameters, which is quite a lot.
  // Since by default asm instructions have 2 to 3 operands it's better to
  // introduce helpers that pass those and fill all the remaining with `_none`.

  //! Emit an instruction.
  ASMJIT_API Error emit(uint32_t instId);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5);

  //! Emit an instruction that has a 32-bit signed immediate operand.
  ASMJIT_API Error emit(uint32_t instId, int o0);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int o1);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int o2);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int o3);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int o4);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int o5);

  //! Emit an instruction that has a 64-bit signed immediate operand.
  ASMJIT_API Error emit(uint32_t instId, int64_t o0);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int64_t o1);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int64_t o2);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int64_t o3);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int64_t o4);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int64_t o5);

  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, unsigned int o0) {
    return emit(instId, static_cast<int64_t>(o0));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, unsigned int o1) {
    return emit(instId, o0, static_cast<int64_t>(o1));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, unsigned int o2) {
    return emit(instId, o0, o1, static_cast<int64_t>(o2));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, unsigned int o3) {
    return emit(instId, o0, o1, o2, static_cast<int64_t>(o3));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, unsigned int o4) {
    return emit(instId, o0, o1, o2, o3, static_cast<int64_t>(o4));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, unsigned int o5) {
    return emit(instId, o0, o1, o2, o3, o4, static_cast<int64_t>(o5));
  }

  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, uint64_t o0) {
    return emit(instId, static_cast<int64_t>(o0));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, uint64_t o1) {
    return emit(instId, o0, static_cast<int64_t>(o1));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, uint64_t o2) {
    return emit(instId, o0, o1, static_cast<int64_t>(o2));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, uint64_t o3) {
    return emit(instId, o0, o1, o2, static_cast<int64_t>(o3));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, uint64_t o4) {
    return emit(instId, o0, o1, o2, o3, static_cast<int64_t>(o4));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, uint64_t o5) {
    return emit(instId, o0, o1, o2, o3, o4, static_cast<int64_t>(o5));
  }

  ASMJIT_INLINE Error emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount) {
    return _emitOpArray(instId, opArray, opCount);
  }

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  CodeInfo _codeInfo;                    //!< Basic information about the code (matches CodeHolder::_codeInfo).
  CodeHolder* _code;                     //!< CodeHolder the CodeEmitter is attached to.
  CodeEmitter* _nextEmitter;             //!< Linked list of `CodeEmitter`s attached to the same \ref CodeHolder.

  uint8_t _type;                         //!< See CodeEmitter::Type.
  uint8_t _destroyed;                    //!< Set by ~CodeEmitter() before calling `_code->detach()`.
  uint8_t _finalized;                    //!< True if the CodeEmitter is finalized (CodeBuilder & CodeCompiler).
  uint8_t _reserved;                     //!< \internal
  Error _lastError;                      //!< Last error code.

  uint32_t _privateData;                 //!< Internal private data used freely by any CodeEmitter.
  uint32_t _globalHints;                 //!< Global hints, always in sync with CodeHolder.
  uint32_t _globalOptions;               //!< Global options, combined with `_options` before used by each instruction.

  uint32_t _options;                     //!< Used to pass instruction options        (affects the next instruction).
  RegOnly _extraReg;                     //!< Extra register (op-mask {k} on AVX-512) (affects the next instruction).
  const char* _inlineComment;            //!< Inline comment of the next instruction  (affects the next instruction).

  Operand_ _none;                        //!< Used to pass unused operands to `_emit()` instead of passing null.
  Reg _nativeGpReg;                      //!< Native GP register with zero id.
  const Reg* _nativeGpArray;             //!< Array of native registers indexed from zero.
};

//! \}

} // asmjit namespace

// [Api-End]
#include "../asmjit_apiend.h"

// [Guard]
#endif // _ASMJIT_BASE_CODEEMITTER_H