Moved logic from x86inst.cpp to x86instimpl.cpp, moved some instruction methods to Inst from X86Inst, added Inst::checkFeatures() for retrieving which CPU features are required to run a given instruction, minor reorganization and asmdb update

This commit is contained in:
kobalicek
2017-03-09 16:57:54 +01:00
parent 0e80d2c3c3
commit e8a80ea958
28 changed files with 1882 additions and 1478 deletions

View File

@@ -138,6 +138,8 @@ cxx_add_source(asmjit ASMJIT_SRC asmjit/base
func.h func.h
globals.cpp globals.cpp
globals.h globals.h
inst.cpp
inst.h
logging.cpp logging.cpp
logging.h logging.h
misc_p.h misc_p.h
@@ -185,6 +187,8 @@ cxx_add_source(asmjit ASMJIT_SRC asmjit/x86
x86internal_p.h x86internal_p.h
x86inst.cpp x86inst.cpp
x86inst.h x86inst.h
x86instimpl.cpp
x86instimpl_p.h
x86logging.cpp x86logging.cpp
x86logging_p.h x86logging_p.h
x86misc.h x86misc.h

View File

@@ -19,6 +19,7 @@
#include "./base/cpuinfo.h" #include "./base/cpuinfo.h"
#include "./base/func.h" #include "./base/func.h"
#include "./base/globals.h" #include "./base/globals.h"
#include "./base/inst.h"
#include "./base/logging.h" #include "./base/logging.h"
#include "./base/operand.h" #include "./base/operand.h"
#include "./base/osutils.h" #include "./base/osutils.h"

View File

@@ -396,7 +396,7 @@ void Assembler::_emitLog(
Logging::formatInstruction( Logging::formatInstruction(
sb, logOptions, sb, logOptions,
this, getArchType(), this, getArchType(),
instId, options, _extraOp, opArray, 6); Inst::Detail(instId, options, _extraReg), opArray, 6);
if ((logOptions & Logger::kOptionBinaryForm) != 0) if ((logOptions & Logger::kOptionBinaryForm) != 0)
Logging::formatLine(sb, _bufferPtr, emittedSize, relSize, imLen, getInlineComment()); Logging::formatLine(sb, _bufferPtr, emittedSize, relSize, imLen, getInlineComment());
@@ -432,10 +432,10 @@ Error Assembler::_emitFailed(
Logging::formatInstruction( Logging::formatInstruction(
sb, 0, sb, 0,
this, getArchType(), this, getArchType(),
instId, options, _extraOp, opArray, 6); Inst::Detail(instId, options, _extraReg), opArray, 6);
resetOptions(); resetOptions();
resetExtraOp(); resetExtraReg();
resetInlineComment(); resetInlineComment();
return setLastError(err, sb.getData()); return setLastError(err, sb.getData());
} }

View File

@@ -544,7 +544,7 @@ Error CodeBuilder::serialize(CodeEmitter* dst) {
case CBNode::kNodeFuncCall: { case CBNode::kNodeFuncCall: {
CBInst* node = node_->as<CBInst>(); CBInst* node = node_->as<CBInst>();
dst->setOptions(node->getOptions()); dst->setOptions(node->getOptions());
dst->setExtraOp(node->getExtraOp()); dst->setExtraReg(node->getExtraReg());
err = dst->emitOpArray(node->getInstId(), node->getOpArray(), node->getOpCount()); err = dst->emitOpArray(node->getInstId(), node->getOpArray(), node->getOpCount());
break; break;
} }

View File

@@ -15,6 +15,7 @@
#include "../base/assembler.h" #include "../base/assembler.h"
#include "../base/codeholder.h" #include "../base/codeholder.h"
#include "../base/constpool.h" #include "../base/constpool.h"
#include "../base/inst.h"
#include "../base/operand.h" #include "../base/operand.h"
#include "../base/utils.h" #include "../base/utils.h"
#include "../base/zone.h" #include "../base/zone.h"
@@ -468,9 +469,8 @@ public:
: CBNode(cb, kNodeInst) { : CBNode(cb, kNodeInst) {
orFlags(kFlagIsRemovable); orFlags(kFlagIsRemovable);
_instId = static_cast<uint16_t>(instId); _instDetail.instId = static_cast<uint16_t>(instId);
_reserved = 0; _instDetail.options = options;
_options = options;
_opCount = static_cast<uint8_t>(opCount); _opCount = static_cast<uint8_t>(opCount);
_opArray = opArray; _opArray = opArray;
@@ -485,39 +485,40 @@ public:
// [Accessors] // [Accessors]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
//! Get the instruction id, see \ref X86Inst::Id. ASMJIT_INLINE Inst::Detail& getInstDetail() noexcept { return _instDetail; }
ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instId; } ASMJIT_INLINE const Inst::Detail& getInstDetail() const noexcept { return _instDetail; }
//! Set the instruction id to `instId`.
//!
//! NOTE: Please do not modify instruction code if you don't know what you
//! are doing. Incorrect instruction code and/or operands can cause random
//! errors in production builds and will most probably trigger assertion
//! failures in debug builds.
ASMJIT_INLINE void setInstId(uint32_t instId) noexcept { _instId = static_cast<uint16_t>(instId); }
//! Whether the instruction is either a jump or a conditional jump likely to //! Get the instruction id, see \ref Inst::Id.
//! be taken. ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instDetail.instId; }
//! Set the instruction id to `instId`, see \ref Inst::Id.
ASMJIT_INLINE void setInstId(uint32_t instId) noexcept { _instDetail.instId = instId; }
//! Whether the instruction is either a jump or a conditional jump likely to be taken.
ASMJIT_INLINE bool isTaken() const noexcept { return hasFlag(kFlagIsTaken); } ASMJIT_INLINE bool isTaken() const noexcept { return hasFlag(kFlagIsTaken); }
//! Get emit options. //! Get emit options.
ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } ASMJIT_INLINE uint32_t getOptions() const noexcept { return _instDetail.options; }
//! Set emit options. //! Set emit options.
ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; } ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _instDetail.options = options; }
//! Add emit options. //! Add emit options.
ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _instDetail.options |= options; }
//! Mask emit options. //! Mask emit options.
ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _options &= options; } ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _instDetail.options &= options; }
//! Clear emit options. //! Clear emit options.
ASMJIT_INLINE void delOptions(uint32_t options) noexcept { _options &= ~options; } ASMJIT_INLINE void delOptions(uint32_t options) noexcept { _instDetail.options &= ~options; }
//! Get if the node has extra operand. //! Get if the node has an extra register operand.
ASMJIT_INLINE bool hasExtraOp() const noexcept { return !_extraOp.isNone(); } ASMJIT_INLINE bool hasExtraOp() const noexcept { return !_instDetail.hasExtraReg(); }
//! Get extra operand operand. //! Get extra register operand.
ASMJIT_INLINE Operand& getExtraOp() noexcept { return _extraOp; } ASMJIT_INLINE RegOnly& getExtraReg() noexcept { return _instDetail.extraReg; }
//! \overload //! \overload
ASMJIT_INLINE const Operand& getExtraOp() const noexcept { return _extraOp; } ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _instDetail.extraReg; }
//! Set extra operand. //! Set extra register operand to `reg`.
ASMJIT_INLINE void setExtraOp(const Operand& extraOp) noexcept { _extraOp = extraOp; } ASMJIT_INLINE void setExtraReg(const Reg& reg) noexcept { _instDetail.extraReg.init(reg); }
//! Set extra register operand to `reg`.
ASMJIT_INLINE void setExtraReg(const RegOnly& reg) noexcept { _instDetail.extraReg.init(reg); }
//! Reset extra register operand.
ASMJIT_INLINE void resetExtraReg() noexcept { _instDetail.extraReg.reset(); }
//! Get operands count. //! Get operands count.
ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; } ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; }
@@ -570,11 +571,9 @@ Update:
// [Members] // [Members]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
uint16_t _instId; //!< Instruction id (architecture dependent). Inst::Detail _instDetail; //!< Instruction id, options, and extra register.
uint8_t _memOpIndex; //!< \internal uint8_t _memOpIndex; //!< \internal
uint8_t _reserved; //!< \internal uint8_t _reserved[7]; //!< \internal
uint32_t _options; //!< Instruction options.
Operand _extraOp; //!< Extra operand (REP {cx} or op-mask {k} on AVX-512).
Operand* _opArray; //!< Instruction operands. Operand* _opArray; //!< Instruction operands.
}; };

View File

@@ -42,8 +42,8 @@ CodeEmitter::CodeEmitter(uint32_t type) noexcept
_globalHints(0), _globalHints(0),
_globalOptions(kOptionMaybeFailureCase), _globalOptions(kOptionMaybeFailureCase),
_options(0), _options(0),
_extraReg(),
_inlineComment(nullptr), _inlineComment(nullptr),
_extraOp(),
_none(), _none(),
_nativeGpReg(), _nativeGpReg(),
_nativeGpArray(nullptr) {} _nativeGpArray(nullptr) {}
@@ -79,9 +79,9 @@ Error CodeEmitter::onDetach(CodeHolder* code) noexcept {
_globalOptions = kOptionMaybeFailureCase; _globalOptions = kOptionMaybeFailureCase;
_options = 0; _options = 0;
_extraReg.reset();
_inlineComment = nullptr; _inlineComment = nullptr;
_extraOp.reset();
_nativeGpReg.reset(); _nativeGpReg.reset();
_nativeGpArray = nullptr; _nativeGpArray = nullptr;
@@ -230,30 +230,6 @@ Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int6
#undef OP #undef OP
// ============================================================================
// [asmjit::CodeEmitter - Validation]
// ============================================================================
Error CodeEmitter::_validate(uint32_t instId, const Operand_* opArray, uint32_t opCount) const noexcept {
#if !defined(ASMJIT_DISABLE_VALIDATION)
uint32_t archType = getArchType();
uint32_t options = getGlobalOptions() | getOptions();
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86Inst::validate(archType, instId, options, _extraOp, opArray, 6);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInst::validate(archType, instId, options, _extraOp, opArray, 6);
#endif
return DebugUtils::errored(kErrorInvalidArch);
#else
return DebugUtils::errored(kErrorFeatureNotEnabled);
#endif // !ASMJIT_DISABLE_VALIDATION
}
} // asmjit namespace } // asmjit namespace
// [Api-End] // [Api-End]

View File

@@ -315,12 +315,16 @@ public:
//! Reset options of the next instruction. //! Reset options of the next instruction.
ASMJIT_INLINE void resetOptions() noexcept { _options = 0; } 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). //! Get an extra operand that will be used by the next instruction (architecture specific).
ASMJIT_INLINE const Operand& getExtraOp() const noexcept { return static_cast<const Operand&>(_extraOp); } ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _extraReg; }
//! Set an extra operand that will be used by the next instruction (architecture specific). //! Set an extra operand that will be used by the next instruction (architecture specific).
ASMJIT_INLINE void setExtraOp(const Operand_& extraOp) noexcept { _extraOp = extraOp; } 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). //! Reset an extra operand that will be used by the next instruction (architecture specific).
ASMJIT_INLINE void resetExtraOp() noexcept { _extraOp.setSignature(0); } ASMJIT_INLINE void resetExtraReg() noexcept { _extraReg.reset(); }
//! Get annotation of the next instruction. //! Get annotation of the next instruction.
ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; }
@@ -457,13 +461,6 @@ public:
return _emitOpArray(instId, opArray, opCount); return _emitOpArray(instId, opArray, opCount);
} }
// --------------------------------------------------------------------------
// [Validation]
// --------------------------------------------------------------------------
//! Validate instruction with current options, called by `_emit()` if validation is enabled.
ASMJIT_API Error _validate(uint32_t instId, const Operand_* opArray, uint32_t opCount) const noexcept;
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [Members] // [Members]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@@ -483,8 +480,9 @@ public:
uint32_t _globalOptions; //!< Global options, combined with `_options` before used by each instruction. 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). 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). const char* _inlineComment; //!< Inline comment of the next instruction (affects the next instruction).
Operand_ _extraOp; //!< Extra operand (op-mask {k} on AVX-512) (affects the next instruction).
Operand_ _none; //!< Used to pass unused operands to `_emit()` instead of passing null. Operand_ _none; //!< Used to pass unused operands to `_emit()` instead of passing null.
Reg _nativeGpReg; //!< Native GP register with zero id. Reg _nativeGpReg; //!< Native GP register with zero id.
const Reg* _nativeGpArray; //!< Array of native registers indexed from zero. const Reg* _nativeGpArray; //!< Array of native registers indexed from zero.

View File

@@ -548,6 +548,8 @@ ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept {
x86CallCpuId(&regs, 0xD, 1); x86CallCpuId(&regs, 0xD, 1);
if (regs.eax & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEOPT); if (regs.eax & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEOPT);
if (regs.eax & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEC);
if (regs.eax & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVES);
} }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------

View File

@@ -19,6 +19,90 @@ namespace asmjit {
//! \addtogroup asmjit_base //! \addtogroup asmjit_base
//! \{ //! \{
// ============================================================================
// [asmjit::CpuFeatures]
// ============================================================================
class CpuFeatures {
public:
typedef uintptr_t BitWord;
enum {
kMaxFeatures = 128,
kBitWordSize = static_cast<int>(sizeof(BitWord)) * 8,
kNumBitWords = kMaxFeatures / kBitWordSize
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CpuFeatures() noexcept { reset(); }
ASMJIT_INLINE CpuFeatures(const CpuFeatures& other) noexcept { init(other); }
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void init(const CpuFeatures& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Get all features as `BitWord` array.
ASMJIT_INLINE BitWord* getBits() noexcept { return _bits; }
//! Get all features as `BitWord` array (const).
ASMJIT_INLINE const BitWord* getBits() const noexcept { return _bits; }
//! Get if feature `feature` is present.
ASMJIT_INLINE bool has(uint32_t feature) const noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
return static_cast<bool>((_bits[idx] >> bit) & 0x1);
}
//! Get if all features as defined by `other` are present.
ASMJIT_INLINE bool hasAll(const CpuFeatures& other) const noexcept {
for (uint32_t i = 0; i < kNumBitWords; i++)
if ((_bits[i] & other._bits[i]) != other._bits[i])
return false;
return true;
}
//! Add a CPU `feature`.
ASMJIT_INLINE CpuFeatures& add(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
_bits[idx] |= static_cast<BitWord>(1) << bit;
return *this;
}
//! Remove a CPU `feature`.
ASMJIT_INLINE CpuFeatures& remove(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
_bits[idx] &= ~(static_cast<BitWord>(1) << bit);
return *this;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
BitWord _bits[kNumBitWords];
};
// ============================================================================ // ============================================================================
// [asmjit::CpuInfo] // [asmjit::CpuInfo]
// ============================================================================ // ============================================================================
@@ -105,8 +189,10 @@ public:
kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention). kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention).
kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention). kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention).
kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256. kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256.
kX86FeatureXSAVE, //!< CPU has XSAVE support - XSAVE/XRSTOR, XSETBV/XGETBV, and XCR. kX86FeatureXSAVE, //!< CPU has XSAVE support (XSAVE/XRSTOR, XSETBV/XGETBV, and XCR).
kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support - XSAVEOPT/XSAVEOPT64. kX86FeatureXSAVEC, //!< CPU has XSAVEC support (XSAVEC).
kX86FeatureXSAVES, //!< CPU has XSAVES support (XSAVES/XRSTORS).
kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support (XSAVEOPT/XSAVEOPT64).
kX86FeatureOSXSAVE, //!< CPU has XSAVE enabled by OS. kX86FeatureOSXSAVE, //!< CPU has XSAVE enabled by OS.
kX86FeatureAVX, //!< CPU has AVX. kX86FeatureAVX, //!< CPU has AVX.
kX86FeatureAVX2, //!< CPU has AVX2. kX86FeatureAVX2, //!< CPU has AVX2.
@@ -140,11 +226,6 @@ public:
kX86FeaturesCount //!< Count of X86/X64 CPU features. kX86FeaturesCount //!< Count of X86/X64 CPU features.
}; };
//! \internal
enum {
kFeaturesPerUInt32 = static_cast<int>(sizeof(uint32_t)) * 8
};
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [ArmInfo] // [ArmInfo]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@@ -168,6 +249,7 @@ public:
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
ASMJIT_INLINE CpuInfo() noexcept { reset(); } ASMJIT_INLINE CpuInfo() noexcept { reset(); }
ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept { init(other); }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [Init / Reset] // [Init / Reset]
@@ -178,7 +260,8 @@ public:
_archInfo.init(archType, archMode); _archInfo.init(archType, archMode);
} }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(CpuInfo)); } ASMJIT_INLINE void init(const CpuInfo& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [Detect] // [Detect]
@@ -197,11 +280,6 @@ public:
//! Get CPU architecture sub-type, see \ArchInfo::SubType. //! Get CPU architecture sub-type, see \ArchInfo::SubType.
ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); }
//! Get CPU vendor string.
ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; }
//! Get CPU brand string.
ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; }
//! Get CPU vendor ID. //! Get CPU vendor ID.
ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; } ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; }
//! Get CPU family ID. //! Get CPU family ID.
@@ -216,26 +294,17 @@ public:
return _hwThreadsCount; return _hwThreadsCount;
} }
//! Get all CPU features.
ASMJIT_INLINE const CpuFeatures& getFeatures() const noexcept { return _features; }
//! Get whether CPU has a `feature`. //! Get whether CPU has a `feature`.
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { return _features.has(feature); }
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
uint32_t pos = feature / kFeaturesPerUInt32;
uint32_t bit = feature % kFeaturesPerUInt32;
return static_cast<bool>((_features[pos] >> bit) & 0x1);
}
//! Add a CPU `feature`. //! Add a CPU `feature`.
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { _features.add(feature); return *this; }
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
uint32_t pos = feature / kFeaturesPerUInt32; //! Get CPU vendor string.
uint32_t bit = feature % kFeaturesPerUInt32; ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; }
//! Get CPU brand string.
_features[pos] |= static_cast<uint32_t>(1) << bit; ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; }
return *this;
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [Accessors - ARM] // [Accessors - ARM]
@@ -277,14 +346,14 @@ public:
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
ArchInfo _archInfo; //!< CPU architecture information. ArchInfo _archInfo; //!< CPU architecture information.
char _vendorString[16]; //!< CPU vendor string.
char _brandString[64]; //!< CPU brand string.
uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor. uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor.
uint32_t _family; //!< CPU family ID. uint32_t _family; //!< CPU family ID.
uint32_t _model; //!< CPU model ID. uint32_t _model; //!< CPU model ID.
uint32_t _stepping; //!< CPU stepping. uint32_t _stepping; //!< CPU stepping.
uint32_t _hwThreadsCount; //!< Number of hardware threads. uint32_t _hwThreadsCount; //!< Number of hardware threads.
uint32_t _features[8]; //!< CPU features (bit-array). CpuFeatures _features; //!< CPU features.
char _vendorString[16]; //!< CPU vendor string.
char _brandString[64]; //!< CPU brand string.
// Architecture specific data. // Architecture specific data.
union { union {

View File

@@ -40,8 +40,6 @@ static const uint64_t kNoBaseAddress = ~static_cast<uint64_t>(0);
//! Global definitions. //! Global definitions.
ASMJIT_ENUM(Defs) { ASMJIT_ENUM(Defs) {
//! Invalid instruction id.
kInvalidInstId = 0,
//! Invalid register id. //! Invalid register id.
kInvalidRegId = 0xFF, kInvalidRegId = 0xFF,
@@ -75,23 +73,6 @@ ASMJIT_ENUM(Limits) {
} // Globals namespace } // Globals namespace
// ============================================================================
// [asmjit::AnyInst]
// ============================================================================
//! Definitions and utilities related to instructions used by all architectures.
namespace AnyInst {
ASMJIT_ENUM(JumpType) {
kJumpTypeNone = 0, //!< Instruction doesn't jump (regular instruction).
kJumpTypeDirect = 1, //!< Instruction is a unconditional (direct) jump.
kJumpTypeConditional = 2, //!< Instruction is a conditional jump.
kJumpTypeCall = 3, //!< Instruction is a function call.
kJumpTypeReturn = 4 //!< Instruction is a function return.
};
} // AnyInst namespace
// ============================================================================ // ============================================================================
// [asmjit::Error] // [asmjit::Error]
// ============================================================================ // ============================================================================

77
src/asmjit/base/inst.cpp Normal file
View File

@@ -0,0 +1,77 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../asmjit_build.h"
#if defined(ASMJIT_BUILD_X86)
// [Dependencies]
#include "../base/arch.h"
#include "../base/inst.h"
#if defined(ASMJIT_BUILD_X86)
# include "../x86/x86instimpl_p.h"
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
# include "../arm/arminstimpl_p.h"
#endif // ASMJIT_BUILD_ARM
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Inst - Validate]
// ============================================================================
#if !defined(ASMJIT_DISABLE_VALIDATION)
Error Inst::validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept {
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86InstImpl::validate(archType, detail, operands, count);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInstImpl::validate(archType, detail, operands, count);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
#endif
// ============================================================================
// [asmjit::Inst - CheckFeatures]
// ============================================================================
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
Error Inst::checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept {
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86InstImpl::checkFeatures(archType, detail, operands, count, out);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInstImpl::checkFeatures(archType, detail, operands, count, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
#endif // !defined(ASMJIT_DISABLE_EXTENSIONS)
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // ASMJIT_BUILD_X86

108
src/asmjit/base/inst.h Normal file
View File

@@ -0,0 +1,108 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_INST_H
#define _ASMJIT_BASE_INST_H
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/operand.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Inst]
// ============================================================================
//! Definitions and utilities related to instructions used by all architectures.
struct Inst {
ASMJIT_ENUM(Id) {
kIdNone = 0 //!< Invalid or uninitialized instruction id.
};
//! Describes an instruction's jump type, if any.
ASMJIT_ENUM(JumpType) {
kJumpTypeNone = 0, //!< Instruction doesn't jump (regular instruction).
kJumpTypeDirect = 1, //!< Instruction is a unconditional (direct) jump.
kJumpTypeConditional = 2, //!< Instruction is a conditional jump.
kJumpTypeCall = 3, //!< Instruction is a function call.
kJumpTypeReturn = 4 //!< Instruction is a function return.
};
// --------------------------------------------------------------------------
// [Detail]
// --------------------------------------------------------------------------
//! Instruction id, options, and extraReg packed in a single structure. This
//! structure exists to simplify analysis and validation API that requires a
//! lot of information about the instruction to be processed.
class Detail {
public:
ASMJIT_INLINE Detail() noexcept
: instId(0),
options(0),
extraReg() {}
explicit ASMJIT_INLINE Detail(uint32_t instId, uint32_t options = 0) noexcept
: instId(instId),
options(options),
extraReg() {}
ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const RegOnly& reg) noexcept
: instId(instId),
options(options),
extraReg(reg) {}
ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const Reg& reg) noexcept
: instId(instId),
options(options) { extraReg.init(reg); }
// ------------------------------------------------------------------------
// [Accessors]
// ------------------------------------------------------------------------
ASMJIT_INLINE bool hasExtraReg() const noexcept { return extraReg.isValid(); }
// ------------------------------------------------------------------------
// [Members]
// ------------------------------------------------------------------------
uint32_t instId;
uint32_t options;
RegOnly extraReg;
};
// --------------------------------------------------------------------------
// [API]
// --------------------------------------------------------------------------
#if !defined(ASMJIT_DISABLE_VALIDATION)
//! Validate the given instruction.
ASMJIT_API static Error validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept;
#endif // !ASMJIT_DISABLE_VALIDATION
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
//! Check CPU features required to execute the given instruction.
ASMJIT_API static Error checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept;
#endif // !defined(ASMJIT_DISABLE_EXTENSIONS)
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_INST_H

View File

@@ -233,17 +233,14 @@ Error Logging::formatInstruction(
uint32_t logOptions, uint32_t logOptions,
const CodeEmitter* emitter, const CodeEmitter* emitter,
uint32_t archType, uint32_t archType,
uint32_t instId, const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept {
uint32_t options,
const Operand_& extraOp,
const Operand_* opArray, uint32_t opCount) noexcept {
#if defined(ASMJIT_BUILD_X86) #if defined(ASMJIT_BUILD_X86)
return X86Logging::formatInstruction(sb, logOptions, emitter, archType, instId, options, extraOp, opArray, opCount); return X86Logging::formatInstruction(sb, logOptions, emitter, archType, detail, opArray, opCount);
#endif // ASMJIT_BUILD_X86 #endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM) #if defined(ASMJIT_BUILD_ARM)
return ArmLogging::formatInstruction(sb, logOptions, emitter, archType, instId, options, extraOp, opArray, opCount); return ArmLogging::formatInstruction(sb, logOptions, emitter, archType, detail, opArray, opCount);
#endif // ASMJIT_BUILD_ARM #endif // ASMJIT_BUILD_ARM
return kErrorInvalidArch; return kErrorInvalidArch;
@@ -372,10 +369,7 @@ Error Logging::formatNode(
ASMJIT_PROPAGATE( ASMJIT_PROPAGATE(
Logging::formatInstruction(sb, logOptions, cb, Logging::formatInstruction(sb, logOptions, cb,
cb->getArchType(), cb->getArchType(),
node->getInstId(), node->getInstDetail(), node->getOpArray(), node->getOpCount()));
node->getOptions(),
node->getExtraOp(),
node->getOpArray(), node->getOpCount()));
break; break;
} }
@@ -436,10 +430,7 @@ Error Logging::formatNode(
ASMJIT_PROPAGATE( ASMJIT_PROPAGATE(
Logging::formatInstruction(sb, logOptions, cb, Logging::formatInstruction(sb, logOptions, cb,
cb->getArchType(), cb->getArchType(),
node->getInstId(), node->getInstDetail(), node->getOpArray(), node->getOpCount()));
node->getOptions(),
node->getExtraOp(),
node->getOpArray(), node->getOpCount()));
break; break;
} }
#endif // !ASMJIT_DISABLE_COMPILER #endif // !ASMJIT_DISABLE_COMPILER

View File

@@ -8,9 +8,8 @@
#ifndef _ASMJIT_BASE_LOGGING_H #ifndef _ASMJIT_BASE_LOGGING_H
#define _ASMJIT_BASE_LOGGING_H #define _ASMJIT_BASE_LOGGING_H
#include "../asmjit_build.h"
// [Dependencies] // [Dependencies]
#include "../base/inst.h"
#include "../base/string.h" #include "../base/string.h"
// [Api-Begin] // [Api-Begin]
@@ -249,10 +248,7 @@ struct Logging {
uint32_t logOptions, uint32_t logOptions,
const CodeEmitter* emitter, const CodeEmitter* emitter,
uint32_t archType, uint32_t archType,
uint32_t instId, const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept;
uint32_t options,
const Operand_& extraOp,
const Operand_* opArray, uint32_t opCount) noexcept;
#if !defined(ASMJIT_DISABLE_BUILDER) #if !defined(ASMJIT_DISABLE_BUILDER)
ASMJIT_API static Error formatNode( ASMJIT_API static Error formatNode(
@@ -262,7 +258,7 @@ struct Logging {
const CBNode* node_) noexcept; const CBNode* node_) noexcept;
#endif // !ASMJIT_DISABLE_BUILDER #endif // !ASMJIT_DISABLE_BUILDER
// Only used by AsmJit internals, not available for users. // Only used by AsmJit internals, not available to users.
#if defined(ASMJIT_EXPORTS) #if defined(ASMJIT_EXPORTS)
enum { enum {
// Has to be big to be able to hold all metadata compiler can assign to a // Has to be big to be able to hold all metadata compiler can assign to a

View File

@@ -709,10 +709,9 @@ public:
//! some operands contains a garbage or other metadata in the upper 8 bytes //! some operands contains a garbage or other metadata in the upper 8 bytes
//! then `isSame()` may return `true` in cases where `isEqual()` returns //! then `isSame()` may return `true` in cases where `isEqual()` returns
//! false. However. no such case is known at the moment. //! false. However. no such case is known at the moment.
ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == _packed[1]; } ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == other._packed[0]; }
//! Get if the register type matches `rType`. //! Get if the register type matches `rType` - same as `isReg(rType)`, provided for convenience.
//! Same as `isReg(rType)`, provided for convenience.
ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); } ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); }
//! Get if the register kind matches `rKind`. //! Get if the register kind matches `rKind`.
ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); } ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); }
@@ -785,6 +784,77 @@ public:
static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); } static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); }
}; };
// ============================================================================
// [asmjit::RegOnly]
// ============================================================================
//! RegOnly is 8-byte version of `Reg` that only allows to store either `Reg`
//! or nothing. This class was designed to decrease the space consumed by each
//! extra "operand" in `CodeEmitter` and `CBInst` classes.
struct RegOnly {
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize the `RegOnly` instance to hold register `signature` and `id`.
ASMJIT_INLINE void init(uint32_t signature, uint32_t id) noexcept {
_signature = signature;
_id = id;
}
ASMJIT_INLINE void init(const Reg& reg) noexcept { init(reg.getSignature(), reg.getId()); }
ASMJIT_INLINE void init(const RegOnly& reg) noexcept { init(reg.getSignature(), reg.getId()); }
//! Reset the `RegOnly` to none.
ASMJIT_INLINE void reset() noexcept { init(0, 0); }
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get if the `ExtraReg` is none (same as calling `Operand_::isNone()`).
ASMJIT_INLINE bool isNone() const noexcept { return _signature == 0; }
//! Get if the register is valid (either virtual or physical).
ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; }
//! Get if this is a physical register.
ASMJIT_INLINE bool isPhysReg() const noexcept { return _id < Globals::kInvalidRegId; }
//! Get if this is a virtual register (used by \ref CodeCompiler).
ASMJIT_INLINE bool isVirtReg() const noexcept { return Operand::isPackedId(_id); }
//! Get register signature or 0.
ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; }
//! Get register id or 0.
ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }
//! \internal
//!
//! Unpacks information from operand's signature.
ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; }
//! Get the register type.
ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(Operand::kSignatureRegTypeBits, Operand::kSignatureRegTypeShift); }
//! Get the register kind.
ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(Operand::kSignatureRegKindBits, Operand::kSignatureRegKindShift); }
// --------------------------------------------------------------------------
// [ToReg]
// --------------------------------------------------------------------------
//! Convert back to `RegT` operand.
template<typename RegT>
ASMJIT_INLINE RegT toReg() const noexcept { return RegT(Init, _signature, _id); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Type of the operand, either `kOpNone` or `kOpReg`.
uint32_t _signature;
//! Physical or virtual register id.
uint32_t _id;
};
// ============================================================================ // ============================================================================
// [asmjit::Mem] // [asmjit::Mem]
// ============================================================================ // ============================================================================

View File

@@ -560,6 +560,7 @@ Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o
if (ASMJIT_UNLIKELY(err)) goto Failed; if (ASMJIT_UNLIKELY(err)) goto Failed;
cursor = _bufferPtr; cursor = _bufferPtr;
options &= ~1;
} }
} }
@@ -582,7 +583,7 @@ Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o
opArray[5].reset(); opArray[5].reset();
} }
err = _validate(instId, opArray, 6); err = Inst::validate(getArchType(), Inst::Detail(instId, options, _extraReg), opArray, 6);
if (ASMJIT_UNLIKELY(err)) goto Failed; if (ASMJIT_UNLIKELY(err)) goto Failed;
} }
#endif // !ASMJIT_DISABLE_VALIDATION #endif // !ASMJIT_DISABLE_VALIDATION
@@ -614,7 +615,7 @@ Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o
if (ASMJIT_UNLIKELY(!(iFlags & (X86Inst::kFlagRep | X86Inst::kFlagRepnz)))) if (ASMJIT_UNLIKELY(!(iFlags & (X86Inst::kFlagRep | X86Inst::kFlagRepnz))))
goto InvalidRepPrefix; goto InvalidRepPrefix;
if (!_extraOp.isNone() && ASMJIT_UNLIKELY(!X86Reg::isGp(_extraOp, X86Gp::kIdCx))) if (_extraReg.isValid() && ASMJIT_UNLIKELY(_extraReg.getKind() != X86Reg::kKindGp || _extraReg.getId() != X86Gp::kIdCx))
goto InvalidRepPrefix; goto InvalidRepPrefix;
EMIT_BYTE((options & X86Inst::kOptionRepnz) ? 0xF2 : 0xF3); EMIT_BYTE((options & X86Inst::kOptionRepnz) ? 0xF2 : 0xF3);
@@ -4041,15 +4042,13 @@ EmitVexEvexR:
// Construct `x` - a complete EVEX|VEX prefix. // Construct `x` - a complete EVEX|VEX prefix.
uint32_t x = ((opReg << 4) & 0xF980U) | // [........|........|Vvvvv..R|R.......]. uint32_t x = ((opReg << 4) & 0xF980U) | // [........|........|Vvvvv..R|R.......].
((rbReg << 2) & 0x0060U) | // [........|........|........|.BB.....]. ((rbReg << 2) & 0x0060U) | // [........|........|........|.BB.....].
(x86ExtractLLMM(opCode, options)); // [........|.LL.....|Vvvvv..R|RBBmmmmm]. (x86ExtractLLMM(opCode, options)) | // [........|.LL.....|Vvvvv..R|RBBmmmmm].
(_extraReg.getId() << 16); // [........|.LL..aaa|Vvvvv..R|RBBmmmmm].
opReg &= 0x7; opReg &= 0x7;
// Mark invalid VEX (force EVEX) case: // [@.......|.LL.....|Vvvvv..R|RBBmmmmm]. // Mark invalid VEX (force EVEX) case: // [@.......|.LL..aaa|Vvvvv..R|RBBmmmmm].
x |= (~commonData->getFlags() & X86Inst::kFlagVex) << (31 - Utils::firstBitOfT<X86Inst::kFlagVex>()); x |= (~commonData->getFlags() & X86Inst::kFlagVex) << (31 - Utils::firstBitOfT<X86Inst::kFlagVex>());
if (X86Reg::isK(_extraOp))
x |= _extraOp.as<Reg>().getId() << 16; // [@.......|.LL..aaa|Vvvvv..R|RBBmmmmm].
// Handle AVX512 options by a single branch. // Handle AVX512 options by a single branch.
const uint32_t kAvx512Options = X86Inst::kOptionZMask | const uint32_t kAvx512Options = X86Inst::kOptionZMask |
X86Inst::kOption1ToX | X86Inst::kOption1ToX |
@@ -4150,15 +4149,13 @@ EmitVexEvexM:
((rxReg << 3 ) & 0x00000040U) | // [........|........|........|.X......]. ((rxReg << 3 ) & 0x00000040U) | // [........|........|........|.X......].
((rxReg << 15) & 0x00080000U) | // [........|....X...|........|........]. ((rxReg << 15) & 0x00080000U) | // [........|....X...|........|........].
((rbReg << 2 ) & 0x00000020U) | // [........|........|........|..B.....]. ((rbReg << 2 ) & 0x00000020U) | // [........|........|........|..B.....].
(x86ExtractLLMM(opCode, options)); // [........|.LL.X...|Vvvvv..R|RXBmmmmm]. (x86ExtractLLMM(opCode, options)) | // [........|.LL.X...|Vvvvv..R|RXBmmmmm].
(_extraReg.getId() << 16) ; // [........|.LL.Xaaa|Vvvvv..R|RXBmmmmm].
opReg &= 0x07U; opReg &= 0x07U;
// Mark invalid VEX (force EVEX) case: // [@.......|.LL.X...|Vvvvv..R|RXBmmmmm]. // Mark invalid VEX (force EVEX) case: // [@.......|.LL.Xaaa|Vvvvv..R|RXBmmmmm].
x |= (~commonData->getFlags() & X86Inst::kFlagVex) << (31 - Utils::firstBitOfT<X86Inst::kFlagVex>()); x |= (~commonData->getFlags() & X86Inst::kFlagVex) << (31 - Utils::firstBitOfT<X86Inst::kFlagVex>());
if (X86Reg::isK(_extraOp))
x |= _extraOp.as<Reg>().getId() << 16; // [@.......|.LL.Xaaa|Vvvvv..R|RXBmmmmm].
// Handle AVX512 options by a single branch. // Handle AVX512 options by a single branch.
const uint32_t kAvx512Options = X86Inst::kOption1ToX | const uint32_t kAvx512Options = X86Inst::kOption1ToX |
X86Inst::kOptionZMask | X86Inst::kOptionZMask |
@@ -4483,7 +4480,7 @@ EmitDone:
#endif // !ASMJIT_DISABLE_LOGGING #endif // !ASMJIT_DISABLE_LOGGING
resetOptions(); resetOptions();
resetExtraOp(); resetExtraReg();
resetInlineComment(); resetInlineComment();
_bufferPtr = cursor; _bufferPtr = cursor;

View File

@@ -124,13 +124,15 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
Operand(o3) Operand(o3)
}; };
Error err = X86Inst::validate(getArchType(), instId, options, _extraOp, opArray, opCount); Inst::Detail instDetail(instId, options, _extraReg);
Error err = Inst::validate(getArchType(), instDetail, opArray, opCount);
if (err) { if (err) {
#if !defined(ASMJIT_DISABLE_LOGGING) #if !defined(ASMJIT_DISABLE_LOGGING)
StringBuilderTmp<256> sb; StringBuilderTmp<256> sb;
sb.appendString(DebugUtils::errorAsString(err)); sb.appendString(DebugUtils::errorAsString(err));
sb.appendString(": "); sb.appendString(": ");
Logging::formatInstruction(sb, 0, this, getArchType(), instId, options, _extraOp, opArray, opCount); Logging::formatInstruction(sb, 0, this, getArchType(), instDetail, opArray, opCount);
return setLastError(err, sb.getData()); return setLastError(err, sb.getData());
#else #else
return setLastError(err); return setLastError(err);
@@ -160,8 +162,8 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
if (opCount > 3) opArray[3].copyFrom(o3); if (opCount > 3) opArray[3].copyFrom(o3);
new(node) CBJump(this, instId, options, opArray, opCount); new(node) CBJump(this, instId, options, opArray, opCount);
node->_extraOp = _extraOp; node->_instDetail.extraReg = _extraReg;
_extraOp.reset(); _extraReg.reset();
CBLabel* jTarget = nullptr; CBLabel* jTarget = nullptr;
if (!(options & kOptionUnfollow)) { if (!(options & kOptionUnfollow)) {
@@ -212,8 +214,8 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
if (opCount > 3) opArray[3].copyFrom(o3); if (opCount > 3) opArray[3].copyFrom(o3);
node = new(node) CBInst(this, instId, options, opArray, opCount); node = new(node) CBInst(this, instId, options, opArray, opCount);
node->_extraOp = _extraOp; node->_instDetail.extraReg = _extraReg;
_extraOp.reset(); _extraReg.reset();
if (inlineComment) { if (inlineComment) {
inlineComment = static_cast<char*>(_cbDataZone.dup(inlineComment, ::strlen(inlineComment), true)); inlineComment = static_cast<char*>(_cbDataZone.dup(inlineComment, ::strlen(inlineComment), true));
@@ -258,13 +260,15 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
Operand(o5) Operand(o5)
}; };
Error err = X86Inst::validate(getArchType(), instId, options, _extraOp, opArray, opCount); Inst::Detail instDetail(instId, options, _extraReg);
Error err = Inst::validate(getArchType(), instDetail, opArray, opCount);
if (err) { if (err) {
#if !defined(ASMJIT_DISABLE_LOGGING) #if !defined(ASMJIT_DISABLE_LOGGING)
StringBuilderTmp<256> sb; StringBuilderTmp<256> sb;
sb.appendString(DebugUtils::errorAsString(err)); sb.appendString(DebugUtils::errorAsString(err));
sb.appendString(": "); sb.appendString(": ");
Logging::formatInstruction(sb, 0, this, getArchType(), instId, options, _extraOp, opArray, opCount); Logging::formatInstruction(sb, 0, this, getArchType(), instDetail, opArray, opCount);
return setLastError(err, sb.getData()); return setLastError(err, sb.getData());
#else #else
return setLastError(err); return setLastError(err);
@@ -296,8 +300,8 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
if (opCount > 5) opArray[5].copyFrom(o5); if (opCount > 5) opArray[5].copyFrom(o5);
new(node) CBJump(this, instId, options, opArray, opCount); new(node) CBJump(this, instId, options, opArray, opCount);
node->_extraOp = _extraOp; node->_instDetail.extraReg = _extraReg;
_extraOp.reset(); _extraReg.reset();
CBLabel* jTarget = nullptr; CBLabel* jTarget = nullptr;
if (!(options & kOptionUnfollow)) { if (!(options & kOptionUnfollow)) {
@@ -350,8 +354,8 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
if (opCount > 5) opArray[5].copyFrom(o5); if (opCount > 5) opArray[5].copyFrom(o5);
node = new(node) CBInst(this, instId, options, opArray, opCount); node = new(node) CBInst(this, instId, options, opArray, opCount);
node->_extraOp = _extraOp; node->_instDetail.extraReg = _extraReg;
_extraOp.reset(); _extraReg.reset();
if (inlineComment) { if (inlineComment) {
inlineComment = static_cast<char*>(_cbDataZone.dup(inlineComment, ::strlen(inlineComment), true)); inlineComment = static_cast<char*>(_cbDataZone.dup(inlineComment, ::strlen(inlineComment), true));

View File

@@ -360,12 +360,12 @@ public:
//! Use REP/REPZ prefix. //! Use REP/REPZ prefix.
ASMJIT_INLINE This& rep(const X86Gp& zcx) noexcept { ASMJIT_INLINE This& rep(const X86Gp& zcx) noexcept {
static_cast<This*>(this)->_extraOp = zcx; static_cast<This*>(this)->_extraReg.init(zcx);
return _addOptions(X86Inst::kOptionRep); return _addOptions(X86Inst::kOptionRep);
} }
//! Use REPNZ prefix. //! Use REPNZ prefix.
ASMJIT_INLINE This& repnz(const X86Gp& zcx) noexcept { ASMJIT_INLINE This& repnz(const X86Gp& zcx) noexcept {
static_cast<This*>(this)->_extraOp = zcx; static_cast<This*>(this)->_extraReg.init(zcx);
return _addOptions(X86Inst::kOptionRepnz); return _addOptions(X86Inst::kOptionRepnz);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,8 @@
#define _ASMJIT_X86_X86INST_H #define _ASMJIT_X86_X86INST_H
// [Dependencies] // [Dependencies]
#include "../base/assembler.h" #include "../base/assembler.h" // TODO: Is that necessary?
#include "../base/inst.h"
#include "../base/operand.h" #include "../base/operand.h"
#include "../base/utils.h" #include "../base/utils.h"
#include "../x86/x86globals.h" #include "../x86/x86globals.h"
@@ -301,7 +302,7 @@ struct X86Inst {
kIdInsertq, // [ANY] {SSE4A} kIdInsertq, // [ANY] {SSE4A}
kIdInt, // [ANY] kIdInt, // [ANY]
kIdInt3, // [ANY] kIdInt3, // [ANY]
kIdInto, // [ANY] kIdInto, // [X86]
kIdInvd, // [ANY] {I486} kIdInvd, // [ANY] {I486}
kIdInvlpg, // [ANY] {I486} kIdInvlpg, // [ANY] {I486}
kIdInvpcid, // [ANY] {I486} kIdInvpcid, // [ANY] {I486}
@@ -1055,8 +1056,8 @@ struct X86Inst {
kIdVmovupd, // [ANY] {AVX|AVX512_F+VL} kIdVmovupd, // [ANY] {AVX|AVX512_F+VL}
kIdVmovups, // [ANY] {AVX|AVX512_F+VL} kIdVmovups, // [ANY] {AVX|AVX512_F+VL}
kIdVmpsadbw, // [ANY] {AVX|AVX2} kIdVmpsadbw, // [ANY] {AVX|AVX2}
kIdVmulpd, // [ANY] {AVX|AVX2|AVX512_F+VL} kIdVmulpd, // [ANY] {AVX|AVX512_F+VL}
kIdVmulps, // [ANY] {AVX|AVX2|AVX512_F+VL} kIdVmulps, // [ANY] {AVX|AVX512_F+VL}
kIdVmulsd, // [ANY] {AVX|AVX512_F} kIdVmulsd, // [ANY] {AVX|AVX512_F}
kIdVmulss, // [ANY] {AVX|AVX512_F} kIdVmulss, // [ANY] {AVX|AVX512_F}
kIdVorpd, // [ANY] {AVX|AVX512_DQ+VL} kIdVorpd, // [ANY] {AVX|AVX512_DQ+VL}
@@ -1426,8 +1427,8 @@ struct X86Inst {
kIdVsqrtsd, // [ANY] {AVX|AVX512_F} kIdVsqrtsd, // [ANY] {AVX|AVX512_F}
kIdVsqrtss, // [ANY] {AVX|AVX512_F} kIdVsqrtss, // [ANY] {AVX|AVX512_F}
kIdVstmxcsr, // [ANY] {AVX} kIdVstmxcsr, // [ANY] {AVX}
kIdVsubpd, // [ANY] {AVX|AVX2|AVX512_F+VL} kIdVsubpd, // [ANY] {AVX|AVX512_F+VL}
kIdVsubps, // [ANY] {AVX|AVX2|AVX512_F+VL} kIdVsubps, // [ANY] {AVX|AVX512_F+VL}
kIdVsubsd, // [ANY] {AVX|AVX512_F} kIdVsubsd, // [ANY] {AVX|AVX512_F}
kIdVsubss, // [ANY] {AVX|AVX512_F} kIdVsubss, // [ANY] {AVX|AVX512_F}
kIdVtestpd, // [ANY] {AVX} kIdVtestpd, // [ANY] {AVX}
@@ -1458,16 +1459,16 @@ struct X86Inst {
kIdXorps, // [ANY] {SSE} kIdXorps, // [ANY] {SSE}
kIdXrstor, // [ANY] {XSAVE} kIdXrstor, // [ANY] {XSAVE}
kIdXrstor64, // [X64] {XSAVE} kIdXrstor64, // [X64] {XSAVE}
kIdXrstors, // [ANY] {XSAVE} kIdXrstors, // [ANY] {XSAVES}
kIdXrstors64, // [X64] {XSAVE} kIdXrstors64, // [X64] {XSAVES}
kIdXsave, // [ANY] {XSAVE} kIdXsave, // [ANY] {XSAVE}
kIdXsave64, // [X64] {XSAVE} kIdXsave64, // [X64] {XSAVE}
kIdXsavec, // [ANY] {XSAVE} kIdXsavec, // [ANY] {XSAVEC}
kIdXsavec64, // [X64] {XSAVE} kIdXsavec64, // [X64] {XSAVEC}
kIdXsaveopt, // [ANY] {XSAVEOPT} kIdXsaveopt, // [ANY] {XSAVEOPT}
kIdXsaveopt64, // [X64] {XSAVEOPT} kIdXsaveopt64, // [X64] {XSAVEOPT}
kIdXsaves, // [ANY] {XSAVE} kIdXsaves, // [ANY] {XSAVES}
kIdXsaves64, // [X64] {XSAVE} kIdXsaves64, // [X64] {XSAVES}
kIdXsetbv, // [ANY] {XSAVE} kIdXsetbv, // [ANY] {XSAVE}
kIdXtest, // [ANY] {TSX} kIdXtest, // [ANY] {TSX}
_kIdCount _kIdCount
@@ -1996,13 +1997,13 @@ struct X86Inst {
kOptionER = 0x00040000U, //!< AVX-512: 'embedded-rounding' {er} and {sae}. kOptionER = 0x00040000U, //!< AVX-512: 'embedded-rounding' {er} and {sae}.
kOptionSAE = 0x00080000U, //!< AVX-512: 'suppress-all-exceptions' {sae}. kOptionSAE = 0x00080000U, //!< AVX-512: 'suppress-all-exceptions' {sae}.
kOption1ToX = 0x00100000U, //!< AVX-512: broadcast the first element to all {1tox}. kOption1ToX = 0x00100000U, //!< AVX-512: broadcast the first element to all {1tox}.
kOptionRN_SAE = 0x00000000U, //!< AVX-512: round-to-nearest (even) {rn-sae} (bits 00). kOptionRN_SAE = 0x00000000U, //!< AVX-512: round-to-nearest (even) {rn-sae} (bits 00).
kOptionRD_SAE = 0x00200000U, //!< AVX-512: round-down (toward -inf) {rd-sae} (bits 01). kOptionRD_SAE = 0x00200000U, //!< AVX-512: round-down (toward -inf) {rd-sae} (bits 01).
kOptionRU_SAE = 0x00400000U, //!< AVX-512: round-up (toward +inf) {ru-sae} (bits 10). kOptionRU_SAE = 0x00400000U, //!< AVX-512: round-up (toward +inf) {ru-sae} (bits 10).
kOptionRZ_SAE = 0x00600000U, //!< AVX-512: round-toward-zero (truncate) {rz-sae} (bits 11). kOptionRZ_SAE = 0x00600000U, //!< AVX-512: round-toward-zero (truncate) {rz-sae} (bits 11).
kOptionZMask = 0x00800000U, //!< AVX-512: Use zeroing {k}{z} instead of merging {k}. kOptionZMask = 0x00800000U, //!< AVX-512: Use zeroing {k}{z} instead of merging {k}.
_kOptionAvx512Mask = 0x00FC0000U, //!< AVX-512: Mask of all possible AVX-512 options except EVEX prefix flag.
_kOptionInvalidRex = 0x01000000U, //!< REX prefix can't be emitted (internal). _kOptionInvalidRex = 0x01000000U, //!< REX prefix can't be emitted (internal).
kOptionOpCodeB = 0x02000000U, //!< REX.B and/or VEX.B field (X64). kOptionOpCodeB = 0x02000000U, //!< REX.B and/or VEX.B field (X64).
@@ -2147,8 +2148,13 @@ struct X86Inst {
ASMJIT_INLINE bool isFpu() const noexcept { return hasFlag(kFlagFpu); } ASMJIT_INLINE bool isFpu() const noexcept { return hasFlag(kFlagFpu); }
//! Get if the instruction is MMX|3DNOW instruction that accesses MMX registers (includes EMMS). //! Get if the instruction is MMX|3DNOW instruction that accesses MMX registers (includes EMMS).
ASMJIT_INLINE bool isMmx() const noexcept { return hasFlag(kFlagMmx); } ASMJIT_INLINE bool isMmx() const noexcept { return hasFlag(kFlagMmx); }
//! Get if the instruction is SSE|AVX|AVX512 instruction that accesses XMM|YMM|ZMM registers (includes VZEROALL|VZEROUPPER). //! Get if the instruction is SSE|AVX|AVX512 instruction that accesses XMM|YMM|ZMM registers (includes VZEROALL|VZEROUPPER).
ASMJIT_INLINE bool isVec() const noexcept { return hasFlag(kFlagVec); } ASMJIT_INLINE bool isVec() const noexcept { return hasFlag(kFlagVec); }
//! Get if the instruction is SSE+ (SSE4.2, AES, SHA included) instruction that accesses XMM registers.
ASMJIT_INLINE bool isSse() const noexcept { return (getFlags() & (kFlagVec | kFlagVex | kFlagEvex)) == kFlagVec; }
//! Get if the instruction is AVX+ (FMA included) instruction that accesses XMM|YMM|ZMM registers.
ASMJIT_INLINE bool isAvx() const noexcept { return isVec() && isVexOrEvex(); }
//! Get if the instruction can be prefixed by LOCK prefix. //! Get if the instruction can be prefixed by LOCK prefix.
ASMJIT_INLINE bool isLockEnabled() const noexcept { return hasFlag(kFlagLock); } ASMJIT_INLINE bool isLockEnabled() const noexcept { return hasFlag(kFlagLock); }
@@ -2167,6 +2173,8 @@ struct X86Inst {
ASMJIT_INLINE bool isVex() const noexcept { return hasFlag(kFlagVex); } ASMJIT_INLINE bool isVex() const noexcept { return hasFlag(kFlagVex); }
//! Get if the instruction uses EVEX (can be set together with VEX if both are encodable). //! Get if the instruction uses EVEX (can be set together with VEX if both are encodable).
ASMJIT_INLINE bool isEvex() const noexcept { return hasFlag(kFlagEvex); } ASMJIT_INLINE bool isEvex() const noexcept { return hasFlag(kFlagEvex); }
//! Get if the instruction uses VEX and/or EVEX.
ASMJIT_INLINE bool isVexOrEvex() const noexcept { return hasFlag(kFlagVex | kFlagEvex); }
//! Get if the instruction supports AVX512 masking {k}. //! Get if the instruction supports AVX512 masking {k}.
ASMJIT_INLINE bool hasAvx512K() const noexcept { return hasFlag(kFlagAvx512K); } ASMJIT_INLINE bool hasAvx512K() const noexcept { return hasFlag(kFlagAvx512K); }
@@ -2184,7 +2192,7 @@ struct X86Inst {
ASMJIT_INLINE bool hasAvx512B64() const noexcept { return hasFlag(kFlagAvx512B64); } ASMJIT_INLINE bool hasAvx512B64() const noexcept { return hasFlag(kFlagAvx512B64); }
//! Get if the instruction may or will jump (returns true also for calls and returns). //! Get if the instruction may or will jump (returns true also for calls and returns).
ASMJIT_INLINE bool doesJump() const noexcept { return _jumpType != AnyInst::kJumpTypeNone; } ASMJIT_INLINE bool doesJump() const noexcept { return _jumpType != Inst::kJumpTypeNone; }
//! Get the destination index of WRITE operation. //! Get the destination index of WRITE operation.
ASMJIT_INLINE uint32_t getWriteIndex() const noexcept { return _writeIndex; } ASMJIT_INLINE uint32_t getWriteIndex() const noexcept { return _writeIndex; }
@@ -2219,7 +2227,7 @@ struct X86Inst {
uint32_t _altOpCodeIndex : 8; //!< Index to table with alternative opcodes. uint32_t _altOpCodeIndex : 8; //!< Index to table with alternative opcodes.
uint32_t _iSignatureIndex :10; //!< First `ISignature` entry in the database. uint32_t _iSignatureIndex :10; //!< First `ISignature` entry in the database.
uint32_t _iSignatureCount : 4; //!< Number of relevant `ISignature` entries. uint32_t _iSignatureCount : 4; //!< Number of relevant `ISignature` entries.
uint32_t _jumpType : 3; //!< Jump type, see `AnyInst::JumpType`. uint32_t _jumpType : 3; //!< Jump type, see `Inst::JumpType`.
uint32_t _singleRegCase : 2; //!< Specifies what happens if all source operands share the same register. uint32_t _singleRegCase : 2; //!< Specifies what happens if all source operands share the same register.
uint32_t _reserved : 5; //!< \internal uint32_t _reserved : 5; //!< \internal
}; };
@@ -2326,8 +2334,13 @@ struct X86Inst {
ASMJIT_INLINE bool isFpu() const noexcept { return getCommonData().isFpu(); } ASMJIT_INLINE bool isFpu() const noexcept { return getCommonData().isFpu(); }
//! Get if the instruction is MMX instruction that accesses MMX registersm, including EMMS. //! Get if the instruction is MMX instruction that accesses MMX registersm, including EMMS.
ASMJIT_INLINE bool isMmx() const noexcept { return getCommonData().isMmx(); } ASMJIT_INLINE bool isMmx() const noexcept { return getCommonData().isMmx(); }
//! Get if the instruction is SSE|AVX|AVX512 instruction that accesses XMM|YMM|ZMM registers. //! Get if the instruction is SSE|AVX|AVX512 instruction that accesses XMM|YMM|ZMM registers.
ASMJIT_INLINE bool isVec() const noexcept { return getCommonData().isVec(); } ASMJIT_INLINE bool isVec() const noexcept { return getCommonData().isVec(); }
//! Get if the instruction is SSE+ (SSE4.2, AES, SHA included) instruction that accesses XMM registers.
ASMJIT_INLINE bool isSse() const noexcept { return getCommonData().isSse(); }
//! Get if the instruction is AVX+ (FMA included) instruction that accesses XMM|YMM|ZMM registers.
ASMJIT_INLINE bool isAvx() const noexcept { return getCommonData().isAvx(); }
//! Get if the instruction can be prefixed by LOCK prefix. //! Get if the instruction can be prefixed by LOCK prefix.
ASMJIT_INLINE bool isLockEnabled() const noexcept { return getCommonData().isLockEnabled(); } ASMJIT_INLINE bool isLockEnabled() const noexcept { return getCommonData().isLockEnabled(); }
@@ -2372,7 +2385,7 @@ struct X86Inst {
// [Get] // [Get]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
//! Get if the `instId` is defined (counts also kInvalidInstId, which is zero). //! Get if the `instId` is defined (counts also Inst::kIdNone, which must be zero).
static ASMJIT_INLINE bool isDefinedId(uint32_t instId) noexcept { return instId < _kIdCount; } static ASMJIT_INLINE bool isDefinedId(uint32_t instId) noexcept { return instId < _kIdCount; }
//! Get instruction information based on the instruction `instId`. //! Get instruction information based on the instruction `instId`.
@@ -2442,17 +2455,6 @@ struct X86Inst {
ASMJIT_API static const char* getNameById(uint32_t instId) noexcept; ASMJIT_API static const char* getNameById(uint32_t instId) noexcept;
#endif // !ASMJIT_DISABLE_TEXT #endif // !ASMJIT_DISABLE_TEXT
// --------------------------------------------------------------------------
// [Validation]
// --------------------------------------------------------------------------
#if !defined(ASMJIT_DISABLE_VALIDATION)
ASMJIT_API static Error validate(
uint32_t archType, uint32_t instId, uint32_t options,
const Operand_& extraOp,
const Operand_* opArray, uint32_t opCount) noexcept;
#endif // !ASMJIT_DISABLE_VALIDATION
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [Members] // [Members]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------

View File

@@ -0,0 +1,731 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/misc_p.h"
#include "../base/utils.h"
#include "../x86/x86instimpl_p.h"
#include "../x86/x86operand.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::X86InstImpl - Validate]
// ============================================================================
#if !defined(ASMJIT_DISABLE_VALIDATION)
template<uint32_t RegType>
struct X86OpTypeFromRegTypeT {
enum {
kValue = (RegType == X86Reg::kRegGpbLo) ? X86Inst::kOpGpbLo :
(RegType == X86Reg::kRegGpbHi) ? X86Inst::kOpGpbHi :
(RegType == X86Reg::kRegGpw ) ? X86Inst::kOpGpw :
(RegType == X86Reg::kRegGpd ) ? X86Inst::kOpGpd :
(RegType == X86Reg::kRegGpq ) ? X86Inst::kOpGpq :
(RegType == X86Reg::kRegXmm ) ? X86Inst::kOpXmm :
(RegType == X86Reg::kRegYmm ) ? X86Inst::kOpYmm :
(RegType == X86Reg::kRegZmm ) ? X86Inst::kOpZmm :
(RegType == X86Reg::kRegRip ) ? X86Inst::kOpNone :
(RegType == X86Reg::kRegSeg ) ? X86Inst::kOpSeg :
(RegType == X86Reg::kRegFp ) ? X86Inst::kOpFp :
(RegType == X86Reg::kRegMm ) ? X86Inst::kOpMm :
(RegType == X86Reg::kRegK ) ? X86Inst::kOpK :
(RegType == X86Reg::kRegBnd ) ? X86Inst::kOpBnd :
(RegType == X86Reg::kRegCr ) ? X86Inst::kOpCr :
(RegType == X86Reg::kRegDr ) ? X86Inst::kOpDr : X86Inst::kOpNone
};
};
template<uint32_t RegType>
struct X86RegMaskFromRegTypeT {
enum {
kMask = (RegType == X86Reg::kRegGpbLo) ? 0x0000000FU :
(RegType == X86Reg::kRegGpbHi) ? 0x0000000FU :
(RegType == X86Reg::kRegGpw ) ? 0x000000FFU :
(RegType == X86Reg::kRegGpd ) ? 0x000000FFU :
(RegType == X86Reg::kRegGpq ) ? 0x000000FFU :
(RegType == X86Reg::kRegXmm ) ? 0x000000FFU :
(RegType == X86Reg::kRegYmm ) ? 0x000000FFU :
(RegType == X86Reg::kRegZmm ) ? 0x000000FFU :
(RegType == X86Reg::kRegRip ) ? 0x00000001U :
(RegType == X86Reg::kRegSeg ) ? 0x0000007EU : // [ES|CS|SS|DS|FS|GS]
(RegType == X86Reg::kRegFp ) ? 0x000000FFU :
(RegType == X86Reg::kRegMm ) ? 0x000000FFU :
(RegType == X86Reg::kRegK ) ? 0x000000FFU :
(RegType == X86Reg::kRegBnd ) ? 0x0000000FU :
(RegType == X86Reg::kRegCr ) ? 0x0000FFFFU :
(RegType == X86Reg::kRegDr ) ? 0x000000FFU : X86Inst::kOpNone
};
};
template<uint32_t RegType>
struct X64RegMaskFromRegTypeT {
enum {
kMask = (RegType == X86Reg::kRegGpbLo) ? 0x0000FFFFU :
(RegType == X86Reg::kRegGpbHi) ? 0x0000000FU :
(RegType == X86Reg::kRegGpw ) ? 0x0000FFFFU :
(RegType == X86Reg::kRegGpd ) ? 0x0000FFFFU :
(RegType == X86Reg::kRegGpq ) ? 0x0000FFFFU :
(RegType == X86Reg::kRegXmm ) ? 0xFFFFFFFFU :
(RegType == X86Reg::kRegYmm ) ? 0xFFFFFFFFU :
(RegType == X86Reg::kRegZmm ) ? 0xFFFFFFFFU :
(RegType == X86Reg::kRegRip ) ? 0x00000001U :
(RegType == X86Reg::kRegSeg ) ? 0x0000007EU : // [ES|CS|SS|DS|FS|GS]
(RegType == X86Reg::kRegFp ) ? 0x000000FFU :
(RegType == X86Reg::kRegMm ) ? 0x000000FFU :
(RegType == X86Reg::kRegK ) ? 0x000000FFU :
(RegType == X86Reg::kRegBnd ) ? 0x0000000FU :
(RegType == X86Reg::kRegCr ) ? 0x0000FFFFU :
(RegType == X86Reg::kRegDr ) ? 0x0000FFFFU : X86Inst::kOpNone
};
};
struct X86ValidationData {
//! Allowed registers by reg-type (X86::kReg...).
uint32_t allowedRegMask[X86Reg::kRegMax + 1];
uint32_t allowedMemBaseRegs;
uint32_t allowedMemIndexRegs;
};
static const uint32_t _x86OpFlagFromRegType[X86Reg::kRegMax + 1] = {
ASMJIT_TABLE_T_32(X86OpTypeFromRegTypeT, kValue, 0)
};
static const X86ValidationData _x86ValidationData = {
{ ASMJIT_TABLE_T_32(X86RegMaskFromRegTypeT, kMask, 0) },
(1U << X86Reg::kRegGpw) | (1U << X86Reg::kRegGpd) | (1U << X86Reg::kRegRip) | (1U << Label::kLabelTag),
(1U << X86Reg::kRegGpw) | (1U << X86Reg::kRegGpd) | (1U << X86Reg::kRegXmm) | (1U << X86Reg::kRegYmm) | (1U << X86Reg::kRegZmm)
};
static const X86ValidationData _x64ValidationData = {
{ ASMJIT_TABLE_T_32(X64RegMaskFromRegTypeT, kMask, 0) },
(1U << X86Reg::kRegGpd) | (1U << X86Reg::kRegGpq) | (1U << X86Reg::kRegRip) | (1U << Label::kLabelTag),
(1U << X86Reg::kRegGpd) | (1U << X86Reg::kRegGpq) | (1U << X86Reg::kRegXmm) | (1U << X86Reg::kRegYmm) | (1U << X86Reg::kRegZmm)
};
static ASMJIT_INLINE bool x86CheckOSig(const X86Inst::OSignature& op, const X86Inst::OSignature& ref, bool& immOutOfRange) noexcept {
// Fail if operand types are incompatible.
uint32_t opFlags = op.flags;
if ((opFlags & ref.flags) == 0) {
// Mark temporarily `immOutOfRange` so we can return a more descriptive error.
if ((opFlags & X86Inst::kOpAllImm) && (ref.flags & X86Inst::kOpAllImm)) {
immOutOfRange = true;
return true;
}
return false;
}
// Fail if memory specific flags and sizes are incompatibles.
uint32_t opMemFlags = op.memFlags;
if (opMemFlags != 0) {
uint32_t refMemFlags = ref.memFlags;
if ((refMemFlags & opMemFlags) == 0)
return false;
if ((refMemFlags & X86Inst::kMemOpBaseOnly) && !(opMemFlags && X86Inst::kMemOpBaseOnly))
return false;
}
// Specific register index.
if (opFlags & X86Inst::kOpAllRegs) {
uint32_t refRegMask = ref.regMask;
if (refRegMask && !(op.regMask & refRegMask))
return false;
}
return true;
}
ASMJIT_FAVOR_SIZE Error X86InstImpl::validate(uint32_t archType, const Inst::Detail& detail, const Operand_* operands, uint32_t count) noexcept {
uint32_t i;
uint32_t archMask;
const X86ValidationData* vd;
if (!ArchInfo::isX86Family(archType))
return DebugUtils::errored(kErrorInvalidArch);
if (archType == ArchInfo::kTypeX86) {
vd = &_x86ValidationData;
archMask = X86Inst::kArchMaskX86;
}
else {
vd = &_x64ValidationData;
archMask = X86Inst::kArchMaskX64;
}
// Get the instruction data.
uint32_t instId = detail.instId;
uint32_t options = detail.options;
if (ASMJIT_UNLIKELY(instId >= X86Inst::_kIdCount))
return DebugUtils::errored(kErrorInvalidArgument);
const X86Inst* iData = &X86InstDB::instData[instId];
uint32_t iFlags = iData->getFlags();
// Validate LOCK, XACQUIRE, and XRELEASE prefixes.
const uint32_t kLockXAcqRel = X86Inst::kOptionXAcquire | X86Inst::kOptionXRelease;
if (options & (X86Inst::kOptionLock | kLockXAcqRel)) {
if (options & X86Inst::kOptionLock) {
if (ASMJIT_UNLIKELY(!(iFlags & X86Inst::kFlagLock) && !(options & kLockXAcqRel)))
return DebugUtils::errored(kErrorInvalidLockPrefix);
if (ASMJIT_UNLIKELY(count < 1 || !operands[0].isMem()))
return DebugUtils::errored(kErrorInvalidLockPrefix);
}
if (options & kLockXAcqRel) {
if (ASMJIT_UNLIKELY(!(options & X86Inst::kOptionLock) || (options & kLockXAcqRel) == kLockXAcqRel))
return DebugUtils::errored(kErrorInvalidPrefixCombination);
if (ASMJIT_UNLIKELY((options & X86Inst::kOptionXAcquire) && !(iFlags & X86Inst::kFlagXAcquire)))
return DebugUtils::errored(kErrorInvalidXAcquirePrefix);
if (ASMJIT_UNLIKELY((options & X86Inst::kOptionXRelease) && !(iFlags & X86Inst::kFlagXRelease)))
return DebugUtils::errored(kErrorInvalidXReleasePrefix);
}
}
// Validate REP and REPNZ prefixes.
const uint32_t kRepRepRepnz = X86Inst::kOptionRep | X86Inst::kOptionRepnz;
if (options & kRepRepRepnz) {
if (ASMJIT_UNLIKELY((options & kRepRepRepnz) == kRepRepRepnz))
return DebugUtils::errored(kErrorInvalidPrefixCombination);
if (ASMJIT_UNLIKELY((options & X86Inst::kOptionRep) && !(iFlags & X86Inst::kFlagRep)))
return DebugUtils::errored(kErrorInvalidRepPrefix);
if (ASMJIT_UNLIKELY((options & X86Inst::kOptionRepnz) && !(iFlags & X86Inst::kFlagRepnz)))
return DebugUtils::errored(kErrorInvalidRepPrefix);
// TODO: Validate extraReg {cx|ecx|rcx}.
}
// Translate the given operands to `X86Inst::OSignature`.
X86Inst::OSignature oSigTranslated[6];
uint32_t combinedOpFlags = 0;
uint32_t combinedRegMask = 0;
const X86Mem* memOp = nullptr;
for (i = 0; i < count; i++) {
const Operand_& op = operands[i];
if (op.getOp() == Operand::kOpNone) break;
uint32_t opFlags = 0;
uint32_t memFlags = 0;
uint32_t regMask = 0;
switch (op.getOp()) {
case Operand::kOpReg: {
uint32_t regType = op.as<Reg>().getType();
if (ASMJIT_UNLIKELY(regType >= X86Reg::kRegCount))
return DebugUtils::errored(kErrorInvalidRegType);
opFlags = _x86OpFlagFromRegType[regType];
if (ASMJIT_UNLIKELY(opFlags == 0))
return DebugUtils::errored(kErrorInvalidRegType);
// If `regId` is equal or greater than Operand::kPackedIdMin it means
// that the register is virtual and its index will be assigned later
// by the register allocator. We must pass unless asked to disallow
// virtual registers.
// TODO: We need an option to refuse virtual regs here.
uint32_t regId = op.getId();
if (regId < Operand::kPackedIdMin) {
if (ASMJIT_UNLIKELY(regId >= 32))
return DebugUtils::errored(kErrorInvalidPhysId);
regMask = Utils::mask(regId);
if (ASMJIT_UNLIKELY((vd->allowedRegMask[regType] & regMask) == 0))
return DebugUtils::errored(kErrorInvalidPhysId);
combinedRegMask |= regMask;
}
else {
regMask = 0xFFFFFFFFU;
}
break;
}
// TODO: Validate base and index and combine with `combinedRegMask`.
case Operand::kOpMem: {
const X86Mem& m = op.as<X86Mem>();
uint32_t baseType = m.getBaseType();
uint32_t indexType = m.getIndexType();
memOp = &m;
if (m.getSegmentId() > 6)
return DebugUtils::errored(kErrorInvalidSegment);
if (baseType) {
uint32_t baseId = m.getBaseId();
if (m.isRegHome()) {
// Home address of virtual register. In such case we don't want to
// validate the type of the base register as it will always be patched
// to ESP|RSP.
}
else {
if (ASMJIT_UNLIKELY((vd->allowedMemBaseRegs & (1U << baseType)) == 0))
return DebugUtils::errored(kErrorInvalidAddress);
}
// Create information that will be validated only if this is an implicit
// memory operand. Basically only usable for string instructions and other
// instructions where memory operand is implicit and has 'seg:[reg]' form.
if (baseId < Operand::kPackedIdMin) {
// Physical base id.
regMask = Utils::mask(baseId);
combinedRegMask |= regMask;
}
else {
// Virtual base id - will the whole mask for implicit mem validation.
// The register is not assigned yet, so we cannot predict the phys id.
regMask = 0xFFFFFFFFU;
}
if (!indexType && !m.getOffsetLo32())
memFlags |= X86Inst::kMemOpBaseOnly;
}
else {
// Base is an address, make sure that the address doesn't overflow 32-bit
// integer (either int32_t or uint32_t) in 32-bit targets.
int64_t offset = m.getOffset();
if (archMask == X86Inst::kArchMaskX86 && !Utils::isInt32(offset) && !Utils::isUInt32(offset))
return DebugUtils::errored(kErrorInvalidAddress);
}
if (indexType) {
if (ASMJIT_UNLIKELY((vd->allowedMemIndexRegs & (1U << indexType)) == 0))
return DebugUtils::errored(kErrorInvalidAddress);
if (indexType == X86Reg::kRegXmm) {
opFlags |= X86Inst::kOpVm;
memFlags |= X86Inst::kMemOpVm32x | X86Inst::kMemOpVm64x;
}
else if (indexType == X86Reg::kRegYmm) {
opFlags |= X86Inst::kOpVm;
memFlags |= X86Inst::kMemOpVm32y | X86Inst::kMemOpVm64y;
}
else if (indexType == X86Reg::kRegZmm) {
opFlags |= X86Inst::kOpVm;
memFlags |= X86Inst::kMemOpVm32z | X86Inst::kMemOpVm64z;
}
else {
opFlags |= X86Inst::kOpMem;
if (baseType)
memFlags |= X86Inst::kMemOpMib;
}
// [RIP + {XMM|YMM|ZMM}] is not allowed.
if (baseType == X86Reg::kRegRip && (opFlags & X86Inst::kOpVm))
return DebugUtils::errored(kErrorInvalidAddress);
uint32_t indexId = m.getIndexId();
if (indexId < Operand::kPackedIdMin)
combinedRegMask |= Utils::mask(indexId);
// Only used for implicit memory operands having 'seg:[reg]' form, so clear it.
regMask = 0;
}
else {
opFlags |= X86Inst::kOpMem;
}
switch (m.getSize()) {
case 0: memFlags |= X86Inst::kMemOpAny ; break;
case 1: memFlags |= X86Inst::kMemOpM8 ; break;
case 2: memFlags |= X86Inst::kMemOpM16 ; break;
case 4: memFlags |= X86Inst::kMemOpM32 ; break;
case 6: memFlags |= X86Inst::kMemOpM48 ; break;
case 8: memFlags |= X86Inst::kMemOpM64 ; break;
case 10: memFlags |= X86Inst::kMemOpM80 ; break;
case 16: memFlags |= X86Inst::kMemOpM128; break;
case 32: memFlags |= X86Inst::kMemOpM256; break;
case 64: memFlags |= X86Inst::kMemOpM512; break;
default:
return DebugUtils::errored(kErrorInvalidOperandSize);
}
break;
}
case Operand::kOpImm: {
uint64_t immValue = op.as<Imm>().getUInt64();
uint32_t immFlags = 0;
if (static_cast<int64_t>(immValue) >= 0) {
const uint32_t k32AndMore = X86Inst::kOpI32 | X86Inst::kOpU32 |
X86Inst::kOpI64 | X86Inst::kOpU64 ;
if (immValue <= 0xFU)
immFlags = X86Inst::kOpU4 | X86Inst::kOpI8 | X86Inst::kOpU8 | X86Inst::kOpI16 | X86Inst::kOpU16 | k32AndMore;
else if (immValue <= 0x7FU)
immFlags = X86Inst::kOpI8 | X86Inst::kOpU8 | X86Inst::kOpI16 | X86Inst::kOpU16 | k32AndMore;
else if (immValue <= 0xFFU)
immFlags = X86Inst::kOpU8 | X86Inst::kOpI16 | X86Inst::kOpU16 | k32AndMore;
else if (immValue <= 0x7FFFU)
immFlags = X86Inst::kOpI16 | X86Inst::kOpU16 | k32AndMore;
else if (immValue <= 0xFFFFU)
immFlags = X86Inst::kOpU16 | k32AndMore;
else if (immValue <= 0x7FFFFFFFU)
immFlags = k32AndMore;
else if (immValue <= 0xFFFFFFFFU)
immFlags = X86Inst::kOpU32 | X86Inst::kOpI64 | X86Inst::kOpU64;
else if (immValue <= ASMJIT_UINT64_C(0x7FFFFFFFFFFFFFFF))
immFlags = X86Inst::kOpI64 | X86Inst::kOpU64;
else
immFlags = X86Inst::kOpU64;
}
else {
// 2s complement negation, as our number is unsigned...
immValue = (~immValue + 1);
if (immValue <= 0x80U)
immFlags = X86Inst::kOpI8 | X86Inst::kOpI16 | X86Inst::kOpI32 | X86Inst::kOpI64;
else if (immValue <= 0x8000U)
immFlags = X86Inst::kOpI16 | X86Inst::kOpI32 | X86Inst::kOpI64;
else if (immValue <= 0x80000000U)
immFlags = X86Inst::kOpI32 | X86Inst::kOpI64;
else
immFlags = X86Inst::kOpI64;
}
opFlags |= immFlags;
break;
}
case Operand::kOpLabel: {
opFlags |= X86Inst::kOpRel8 | X86Inst::kOpRel32;
break;
}
default:
return DebugUtils::errored(kErrorInvalidState);
}
X86Inst::OSignature& tod = oSigTranslated[i];
tod.flags = opFlags;
tod.memFlags = static_cast<uint16_t>(memFlags);
tod.regMask = static_cast<uint8_t>(regMask & 0xFFU);
combinedOpFlags |= opFlags;
}
// Decrease the number of operands of those that are none. This is important
// as Assembler and CodeCompiler may just pass more operands where some of
// them are none (it means that no operand is given at that index). However,
// validate that there are no gaps (like [reg, none, reg] or [none, reg]).
if (i < count) {
while (--count > i)
if (ASMJIT_UNLIKELY(!operands[count].isNone()))
return DebugUtils::errored(kErrorInvalidState);
}
// Validate X86 and X64 specific cases.
if (archMask == X86Inst::kArchMaskX86) {
// Illegal use of 64-bit register in 32-bit mode.
if (ASMJIT_UNLIKELY((combinedOpFlags & X86Inst::kOpGpq) != 0))
return DebugUtils::errored(kErrorInvalidUseOfGpq);
}
else {
// Illegal use of a high 8-bit register with REX prefix.
if (ASMJIT_UNLIKELY((combinedOpFlags & X86Inst::kOpGpbHi) != 0 && (combinedRegMask & 0xFFFFFF00U) != 0))
return DebugUtils::errored(kErrorInvalidUseOfGpbHi);
}
// Validate instruction operands.
const X86Inst::CommonData* commonData = &iData->getCommonData();
const X86Inst::ISignature* iSig = X86InstDB::iSignatureData + commonData->_iSignatureIndex;
const X86Inst::ISignature* iEnd = iSig + commonData->_iSignatureCount;
if (iSig != iEnd) {
const X86Inst::OSignature* oSigData = X86InstDB::oSignatureData;
// If set it means that we matched a signature where only immediate value
// was out of bounds. We can return a more descriptive error if we know this.
bool globalImmOutOfRange = false;
do {
// Check if the architecture is compatible.
if ((iSig->archMask & archMask) == 0) continue;
// Compare the operands table with reference operands.
uint32_t j = 0;
uint32_t iSigCount = iSig->opCount;
bool localImmOutOfRange = false;
if (iSigCount == count) {
for (j = 0; j < count; j++)
if (!x86CheckOSig(oSigTranslated[j], oSigData[iSig->operands[j]], localImmOutOfRange))
break;
}
else if (iSigCount - iSig->implicit == count) {
uint32_t r = 0;
for (j = 0; j < count && r < iSigCount; j++, r++) {
const X86Inst::OSignature* oChk = oSigTranslated + j;
const X86Inst::OSignature* oRef;
Next:
oRef = oSigData + iSig->operands[r];
// Skip implicit.
if ((oRef->flags & X86Inst::kOpImplicit) != 0) {
if (++r >= iSigCount)
break;
else
goto Next;
}
if (!x86CheckOSig(*oChk, *oRef, localImmOutOfRange))
break;
}
}
if (j == count) {
if (!localImmOutOfRange) {
// Match, must clear possible `globalImmOutOfRange`.
globalImmOutOfRange = false;
break;
}
globalImmOutOfRange = localImmOutOfRange;
}
} while (++iSig != iEnd);
if (iSig == iEnd) {
if (globalImmOutOfRange)
return DebugUtils::errored(kErrorInvalidImmediate);
else
return DebugUtils::errored(kErrorInvalidInstruction);
}
}
// Validate AVX-512 options:
const RegOnly& extraReg = detail.extraReg;
const uint32_t kAvx512Options = X86Inst::kOptionZMask |
X86Inst::kOption1ToX |
X86Inst::kOptionER |
X86Inst::kOptionSAE ;
if (!extraReg.isNone() || (options & kAvx512Options)) {
if (commonData->hasFlag(X86Inst::kFlagEvex)) {
// Validate AVX-512 {k} and {k}{z}.
if (!extraReg.isNone()) {
// Mask can only be specified by a 'k' register.
if (ASMJIT_UNLIKELY(extraReg.getType() != X86Reg::kRegK))
return DebugUtils::errored(kErrorInvalidKMaskReg);
if (ASMJIT_UNLIKELY(!commonData->hasAvx512K()))
return DebugUtils::errored(kErrorInvalidKMaskUse);
}
if ((options & X86Inst::kOptionZMask)) {
if (ASMJIT_UNLIKELY((options & X86Inst::kOptionZMask) != 0 && !commonData->hasAvx512Z()))
return DebugUtils::errored(kErrorInvalidKZeroUse);
}
// Validate AVX-512 broadcast {1tox}.
if (options & X86Inst::kOption1ToX) {
if (ASMJIT_UNLIKELY(!memOp))
return DebugUtils::errored(kErrorInvalidBroadcast);
uint32_t size = memOp->getSize();
if (size != 0) {
// The the size is specified it has to match the broadcast size.
if (ASMJIT_UNLIKELY(commonData->hasAvx512B32() && size != 4))
return DebugUtils::errored(kErrorInvalidBroadcast);
if (ASMJIT_UNLIKELY(commonData->hasAvx512B64() && size != 8))
return DebugUtils::errored(kErrorInvalidBroadcast);
}
}
// Validate AVX-512 {sae} and {er}.
if (options & (X86Inst::kOptionSAE | X86Inst::kOptionER)) {
// Rounding control is impossible if the instruction is not reg-to-reg.
if (ASMJIT_UNLIKELY(memOp))
return DebugUtils::errored(kErrorInvalidEROrSAE);
// Check if {sae} or {er} is supported by the instruction.
if (options & X86Inst::kOptionER) {
// NOTE: if both {sae} and {er} are set, we don't care, as {sae} is implied.
if (ASMJIT_UNLIKELY(!commonData->hasAvx512ER()))
return DebugUtils::errored(kErrorInvalidEROrSAE);
// {er} is defined for scalar ops or vector ops using zmm (LL = 10). We
// don't need any more bits in the instruction database to be able to
// validate this, as each AVX512 instruction that has broadcast is vector
// instruction (in this case we require zmm registers), otherwise it's a
// scalar instruction, which is valid.
if (commonData->hasAvx512B()) {
// Supports broadcast, thus we require LL to be '10', which means there
// have to be zmm registers used. We don't calculate LL here, but we know
// that it would be '10' if there is at least one ZMM register used.
// There is no 'ER' enabled instruction with less than two operands.
ASMJIT_ASSERT(count >= 2);
if (ASMJIT_UNLIKELY(!X86Reg::isZmm(operands[0]) && !X86Reg::isZmm(operands[1])))
return DebugUtils::errored(kErrorInvalidEROrSAE);
}
}
else {
// {sae} doesn't have the same limitations as {er}, this is enough.
if (ASMJIT_UNLIKELY(!commonData->hasAvx512SAE()))
return DebugUtils::errored(kErrorInvalidEROrSAE);
}
}
}
else {
// Not AVX512 instruction - maybe OpExtra is xCX register used by REP/REPNZ prefix. Otherwise the instruction is invalid.
if ((options & kAvx512Options) || (options & (X86Inst::kOptionRep | X86Inst::kOptionRepnz)) == 0)
return DebugUtils::errored(kErrorInvalidInstruction);
}
}
return kErrorOk;
}
#endif
// ============================================================================
// [asmjit::X86InstImpl - CheckFeatures]
// ============================================================================
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
ASMJIT_FAVOR_SIZE static uint32_t x86GetRegTypesMask(const Operand_* operands, uint32_t count) noexcept {
uint32_t mask = 0;
for (uint32_t i = 0; i < count; i++) {
const Operand_& op = operands[i];
if (op.isReg()) {
const Reg& reg = op.as<Reg>();
mask |= Utils::mask(reg.getType());
}
else if (op.isMem()) {
const Mem& mem = op.as<Mem>();
if (mem.hasBaseReg()) mask |= Utils::mask(mem.getBaseType());
if (mem.hasIndexReg()) mask |= Utils::mask(mem.getIndexType());
}
}
return mask;
}
ASMJIT_FAVOR_SIZE Error X86InstImpl::checkFeatures(uint32_t archType, const Inst::Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept {
if (!ArchInfo::isX86Family(archType))
return DebugUtils::errored(kErrorInvalidArch);
// Get the instruction data.
uint32_t instId = detail.instId;
if (ASMJIT_UNLIKELY(instId >= X86Inst::_kIdCount))
return DebugUtils::errored(kErrorInvalidArgument);
const X86Inst* iData = &X86InstDB::instData[instId];
const X86Inst::OperationData& od = iData->getOperationData();
const uint8_t* fData = od.getFeaturesData();
const uint8_t* fEnd = od.getFeaturesEnd();
// Copy all features to `out`.
out.reset();
do {
uint32_t feature = fData[0];
if (!feature)
break;
out.add(feature);
} while (++fData != fEnd);
// Since AsmJit merges all instructions that share the same name we have to
// deal with some special cases and also with MMX/SSE and AVX/AVX2 overlaps.
// Only proceed if there were some CPU flags set.
if (fData != od.getFeaturesData()) {
uint32_t mask = x86GetRegTypesMask(operands, count);
// Check for MMX vs SSE overlap.
if (out.has(CpuInfo::kX86FeatureMMX) || out.has(CpuInfo::kX86FeatureMMX2)) {
// Only instructions defined by SSE and SSE2 overlap. Instructions introduced
// by newer instruction sets like SSE3+ don't state MMX as they require SSE3+.
if (out.has(CpuInfo::kX86FeatureSSE) || out.has(CpuInfo::kX86FeatureSSE2)) {
if (!(mask & Utils::mask(X86Reg::kRegXmm))) {
// The instruction doesn't use XMM register(s), thus it's MMX/MMX2 only.
out.remove(CpuInfo::kX86FeatureSSE);
out.remove(CpuInfo::kX86FeatureSSE2);
}
else {
out.remove(CpuInfo::kX86FeatureMMX);
out.remove(CpuInfo::kX86FeatureMMX2);
}
// Special case: PEXTRW instruction is MMX/SSE2 instruction. However, this
// instruction couldn't access memory (only register to register extract) so
// when SSE4.1 introduced the whole family of PEXTR/PINSR instructions they
// also introduced PEXTRW with a new opcode 0x15 that can extract directly to
// memory. This instruction is, of course, not compatible with MMX/SSE2 one.
if (instId == X86Inst::kIdPextrw && count > 0 && !operands[0].isMem()) {
out.remove(CpuInfo::kX86FeatureSSE4_1);
}
}
}
// Check for AVX vs AVX2 overlap.
if (out.has(CpuInfo::kX86FeatureAVX) && out.has(CpuInfo::kX86FeatureAVX2)) {
bool isAVX2 = true;
// Special case: VBROADCASTSS and VBROADCASTSD were introduced in AVX, but
// only version that uses memory as a source operand. AVX2 then added support
// for register source operand.
if (instId == X86Inst::kIdVbroadcastss || instId == X86Inst::kIdVbroadcastsd) {
if (count > 1 && operands[0].isMem())
isAVX2 = false;
}
else {
// AVX instruction set doesn't support integer operations on YMM registers
// as these were later introcuced by AVX2. In our case we have to check if
// YMM register(s) are in use and if that is the case this is an AVX2 instruction.
if (!(mask & Utils::mask(X86Reg::kRegYmm, X86Reg::kRegZmm)))
isAVX2 = false;
}
if (isAVX2)
out.remove(CpuInfo::kX86FeatureAVX);
else
out.remove(CpuInfo::kX86FeatureAVX2);
}
// Check for AVX|AVX2|FMA|F16C vs AVX512 overlap.
if (out.has(CpuInfo::kX86FeatureAVX) || out.has(CpuInfo::kX86FeatureAVX2) || out.has(CpuInfo::kX86FeatureFMA) || out.has(CpuInfo::kX86FeatureF16C)) {
// Only AVX512-F|BW|DQ allow to encode AVX/AVX2 instructions
if (out.has(CpuInfo::kX86FeatureAVX512_F) || out.has(CpuInfo::kX86FeatureAVX512_BW) || out.has(CpuInfo::kX86FeatureAVX512_DQ)) {
uint32_t options = detail.options;
uint32_t kAvx512Options = X86Inst::kOptionEvex | X86Inst::_kOptionAvx512Mask;
if (!(mask & Utils::mask(X86Reg::kRegZmm, X86Reg::kRegK)) && !(options & (kAvx512Options)) && detail.extraReg.getType() != X86Reg::kRegK) {
out.remove(CpuInfo::kX86FeatureAVX512_F)
.remove(CpuInfo::kX86FeatureAVX512_BW)
.remove(CpuInfo::kX86FeatureAVX512_DQ)
.remove(CpuInfo::kX86FeatureAVX512_VL);
}
}
}
// Remove or keep AVX512_VL feature.
if (out.has(CpuInfo::kX86FeatureAVX512_VL)) {
if (!(mask & Utils::mask(X86Reg::kRegZmm)))
out.remove(CpuInfo::kX86FeatureAVX512_VL);
}
}
return kErrorOk;
}
#endif
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"

View File

@@ -0,0 +1,45 @@
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_X86_X86INSTIMPL_P_H
#define _ASMJIT_X86_X86INSTIMPL_P_H
// [Dependencies]
#include "../x86/x86inst.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_x86
//! \{
//! \internal
//!
//! Contains X86/X64 specific implementation of APIs provided by `asmjit::Inst`.
//!
//! The purpose of `X86InstImpl` is to move most of the logic out of `X86Inst`.
struct X86InstImpl {
#if !defined(ASMJIT_DISABLE_VALIDATION)
static Error validate(uint32_t archType, const Inst::Detail& detail, const Operand_* operands, uint32_t count) noexcept;
#endif
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
static Error checkFeatures(uint32_t archType, const Inst::Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept;
#endif
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_X86_X86INSTIMPL_P_H

View File

@@ -725,7 +725,7 @@ ASMJIT_FAVOR_SIZE Error X86Internal::emitRegMove(X86Emitter* emitter,
Operand dst(dst_); Operand dst(dst_);
Operand src(src_); Operand src(src_);
uint32_t instId = Globals::kInvalidInstId; uint32_t instId = Inst::kIdNone;
uint32_t memFlags = 0; uint32_t memFlags = 0;
enum MemFlags { enum MemFlags {
@@ -831,7 +831,7 @@ ASMJIT_FAVOR_SIZE Error X86Internal::emitArgMove(X86Emitter* emitter,
uint32_t dstSize = TypeId::sizeOf(dstTypeId); uint32_t dstSize = TypeId::sizeOf(dstTypeId);
uint32_t srcSize = TypeId::sizeOf(srcTypeId); uint32_t srcSize = TypeId::sizeOf(srcTypeId);
int32_t instId = Globals::kInvalidInstId; int32_t instId = Inst::kIdNone;
// Not a real loop, just 'break' is nicer than 'goto'. // Not a real loop, just 'break' is nicer than 'goto'.
for (;;) { for (;;) {

View File

@@ -580,10 +580,10 @@ ASMJIT_FAVOR_SIZE Error X86Logging::formatInstruction(
uint32_t logOptions, uint32_t logOptions,
const CodeEmitter* emitter, const CodeEmitter* emitter,
uint32_t archType, uint32_t archType,
uint32_t instId, const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept {
uint32_t options,
const Operand_& extraOp, uint32_t instId = detail.instId;
const Operand_* opArray, uint32_t opCount) noexcept { uint32_t options = detail.options;
// Format instruction options and instruction mnemonic. // Format instruction options and instruction mnemonic.
if (instId < X86Inst::_kIdCount) { if (instId < X86Inst::_kIdCount) {
@@ -601,9 +601,9 @@ ASMJIT_FAVOR_SIZE Error X86Logging::formatInstruction(
// REP|REPNZ options. // REP|REPNZ options.
if (options & (X86Inst::kOptionRep | X86Inst::kOptionRepnz)) { if (options & (X86Inst::kOptionRep | X86Inst::kOptionRepnz)) {
sb.appendString((options & X86Inst::kOptionRep) ? "rep " : "repnz "); sb.appendString((options & X86Inst::kOptionRep) ? "rep " : "repnz ");
if (!extraOp.isNone()) { if (detail.hasExtraReg()) {
ASMJIT_PROPAGATE(sb.appendChar('{')); ASMJIT_PROPAGATE(sb.appendChar('{'));
ASMJIT_PROPAGATE(formatOperand(sb, logOptions, emitter, archType, extraOp)); ASMJIT_PROPAGATE(formatOperand(sb, logOptions, emitter, archType, detail.extraReg.toReg<Reg>()));
ASMJIT_PROPAGATE(sb.appendString("} ")); ASMJIT_PROPAGATE(sb.appendString("} "));
} }
} }
@@ -654,9 +654,9 @@ ASMJIT_FAVOR_SIZE Error X86Logging::formatInstruction(
// Support AVX-512 {k}{z}. // Support AVX-512 {k}{z}.
if (i == 0) { if (i == 0) {
if (X86Reg::isK(extraOp)) { if (detail.extraReg.getKind() == X86Reg::kKindK) {
ASMJIT_PROPAGATE(sb.appendString(" {")); ASMJIT_PROPAGATE(sb.appendString(" {"));
ASMJIT_PROPAGATE(formatOperand(sb, logOptions, emitter, archType, extraOp)); ASMJIT_PROPAGATE(formatOperand(sb, logOptions, emitter, archType, detail.extraReg.toReg<Reg>()));
ASMJIT_PROPAGATE(sb.appendChar('}')); ASMJIT_PROPAGATE(sb.appendChar('}'));
if (options & X86Inst::kOptionZMask) if (options & X86Inst::kOptionZMask)

View File

@@ -48,10 +48,7 @@ struct X86Logging {
uint32_t logOptions, uint32_t logOptions,
const CodeEmitter* emitter, const CodeEmitter* emitter,
uint32_t archType, uint32_t archType,
uint32_t instId, const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept;
uint32_t options,
const Operand_& extraOp,
const Operand_* opArray, uint32_t opCount) noexcept;
}; };
//! \} //! \}

View File

@@ -1723,9 +1723,9 @@ _NextGroup:
_avxEnabled = true; _avxEnabled = true;
} }
const Operand_& extraOp = node->getExtraOp(); const RegOnly& extraReg = node->getExtraReg();
if (extraOp.isReg()) { if (extraReg.isValid()) {
uint32_t id = extraOp.as<Reg>().getId(); uint32_t id = extraReg.getId();
if (cc()->isVirtRegValid(id)) { if (cc()->isVirtRegValid(id)) {
VirtReg* vreg = cc()->getVirtRegById(id); VirtReg* vreg = cc()->getVirtRegById(id);
TiedReg* tied; TiedReg* tied;
@@ -2117,10 +2117,7 @@ Error X86RAPass::annotate() {
0, 0,
cc(), cc(),
cc()->getArchType(), cc()->getArchType(),
node->getInstId(), node->getInstDetail(), node->getOpArray(), node->getOpCount());
node->getOptions(),
node->getExtraOp(),
node->getOpArray(), node->getOpCount());
node_->setInlineComment( node_->setInlineComment(
static_cast<char*>(dataZone.dup(sb.getData(), sb.getLength(), true))); static_cast<char*>(dataZone.dup(sb.getData(), sb.getLength(), true)));
@@ -2423,8 +2420,11 @@ Error X86VarAlloc::run(CBNode* node_) {
// Translate node operands. // Translate node operands.
if (node_->getType() == CBNode::kNodeInst) { if (node_->getType() == CBNode::kNodeInst) {
CBInst* node = static_cast<CBInst*>(node_); CBInst* node = static_cast<CBInst*>(node_);
if (node->hasExtraOp()) if (node->hasExtraOp()) {
ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, &node->_extraOp, 1)); Reg reg = node->getExtraReg().toReg<Reg>();
ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, &reg, 1));
node->setExtraReg(reg);
}
ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, node->getOpArray(), node->getOpCount())); ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, node->getOpArray(), node->getOpCount()));
} }
else if (node_->getType() == CBNode::kNodePushArg) { else if (node_->getType() == CBNode::kNodePushArg) {

View File

@@ -114,6 +114,8 @@ static void dumpCpu(void) {
{ CpuInfo::kX86FeatureSMEP , "SMEP" }, { CpuInfo::kX86FeatureSMEP , "SMEP" },
{ CpuInfo::kX86FeatureSHA , "SHA" }, { CpuInfo::kX86FeatureSHA , "SHA" },
{ CpuInfo::kX86FeatureXSAVE , "XSAVE" }, { CpuInfo::kX86FeatureXSAVE , "XSAVE" },
{ CpuInfo::kX86FeatureXSAVEC , "XSAVEC" },
{ CpuInfo::kX86FeatureXSAVES , "XSAVES" },
{ CpuInfo::kX86FeatureXSAVEOPT , "XSAVEOPT" }, { CpuInfo::kX86FeatureXSAVEOPT , "XSAVEOPT" },
{ CpuInfo::kX86FeatureOSXSAVE , "OSXSAVE" }, { CpuInfo::kX86FeatureOSXSAVE , "OSXSAVE" },
{ CpuInfo::kX86FeatureAVX , "AVX" }, { CpuInfo::kX86FeatureAVX , "AVX" },

View File

@@ -1639,7 +1639,7 @@ class X86Generator extends base.BaseGenerator {
} }
var s = `#define F(VAL) X86Inst::kFlag##VAL\n` + var s = `#define F(VAL) X86Inst::kFlag##VAL\n` +
`#define JUMP_TYPE(VAL) AnyInst::kJumpType##VAL\n` + `#define JUMP_TYPE(VAL) Inst::kJumpType##VAL\n` +
`#define SINGLE_REG(VAL) X86Inst::kSingleReg##VAL\n` + `#define SINGLE_REG(VAL) X86Inst::kSingleReg##VAL\n` +
`const X86Inst::CommonData X86InstDB::commonData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` + `const X86Inst::CommonData X86InstDB::commonData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
`#undef SINGLE_REG\n` + `#undef SINGLE_REG\n` +