mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-17 20:44:37 +03:00
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:
@@ -138,6 +138,8 @@ cxx_add_source(asmjit ASMJIT_SRC asmjit/base
|
||||
func.h
|
||||
globals.cpp
|
||||
globals.h
|
||||
inst.cpp
|
||||
inst.h
|
||||
logging.cpp
|
||||
logging.h
|
||||
misc_p.h
|
||||
@@ -185,6 +187,8 @@ cxx_add_source(asmjit ASMJIT_SRC asmjit/x86
|
||||
x86internal_p.h
|
||||
x86inst.cpp
|
||||
x86inst.h
|
||||
x86instimpl.cpp
|
||||
x86instimpl_p.h
|
||||
x86logging.cpp
|
||||
x86logging_p.h
|
||||
x86misc.h
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "./base/cpuinfo.h"
|
||||
#include "./base/func.h"
|
||||
#include "./base/globals.h"
|
||||
#include "./base/inst.h"
|
||||
#include "./base/logging.h"
|
||||
#include "./base/operand.h"
|
||||
#include "./base/osutils.h"
|
||||
|
||||
@@ -396,7 +396,7 @@ void Assembler::_emitLog(
|
||||
Logging::formatInstruction(
|
||||
sb, logOptions,
|
||||
this, getArchType(),
|
||||
instId, options, _extraOp, opArray, 6);
|
||||
Inst::Detail(instId, options, _extraReg), opArray, 6);
|
||||
|
||||
if ((logOptions & Logger::kOptionBinaryForm) != 0)
|
||||
Logging::formatLine(sb, _bufferPtr, emittedSize, relSize, imLen, getInlineComment());
|
||||
@@ -432,10 +432,10 @@ Error Assembler::_emitFailed(
|
||||
Logging::formatInstruction(
|
||||
sb, 0,
|
||||
this, getArchType(),
|
||||
instId, options, _extraOp, opArray, 6);
|
||||
Inst::Detail(instId, options, _extraReg), opArray, 6);
|
||||
|
||||
resetOptions();
|
||||
resetExtraOp();
|
||||
resetExtraReg();
|
||||
resetInlineComment();
|
||||
return setLastError(err, sb.getData());
|
||||
}
|
||||
|
||||
@@ -544,7 +544,7 @@ Error CodeBuilder::serialize(CodeEmitter* dst) {
|
||||
case CBNode::kNodeFuncCall: {
|
||||
CBInst* node = node_->as<CBInst>();
|
||||
dst->setOptions(node->getOptions());
|
||||
dst->setExtraOp(node->getExtraOp());
|
||||
dst->setExtraReg(node->getExtraReg());
|
||||
err = dst->emitOpArray(node->getInstId(), node->getOpArray(), node->getOpCount());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../base/assembler.h"
|
||||
#include "../base/codeholder.h"
|
||||
#include "../base/constpool.h"
|
||||
#include "../base/inst.h"
|
||||
#include "../base/operand.h"
|
||||
#include "../base/utils.h"
|
||||
#include "../base/zone.h"
|
||||
@@ -468,9 +469,8 @@ public:
|
||||
: CBNode(cb, kNodeInst) {
|
||||
|
||||
orFlags(kFlagIsRemovable);
|
||||
_instId = static_cast<uint16_t>(instId);
|
||||
_reserved = 0;
|
||||
_options = options;
|
||||
_instDetail.instId = static_cast<uint16_t>(instId);
|
||||
_instDetail.options = options;
|
||||
|
||||
_opCount = static_cast<uint8_t>(opCount);
|
||||
_opArray = opArray;
|
||||
@@ -485,39 +485,40 @@ public:
|
||||
// [Accessors]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Get the instruction id, see \ref X86Inst::Id.
|
||||
ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instId; }
|
||||
//! 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); }
|
||||
ASMJIT_INLINE Inst::Detail& getInstDetail() noexcept { return _instDetail; }
|
||||
ASMJIT_INLINE const Inst::Detail& getInstDetail() const noexcept { return _instDetail; }
|
||||
|
||||
//! Whether the instruction is either a jump or a conditional jump likely to
|
||||
//! be taken.
|
||||
//! Get the instruction id, see \ref Inst::Id.
|
||||
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); }
|
||||
|
||||
//! 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.
|
||||
ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; }
|
||||
ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _instDetail.options = 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.
|
||||
ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _options &= options; }
|
||||
ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _instDetail.options &= 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.
|
||||
ASMJIT_INLINE bool hasExtraOp() const noexcept { return !_extraOp.isNone(); }
|
||||
//! Get extra operand operand.
|
||||
ASMJIT_INLINE Operand& getExtraOp() noexcept { return _extraOp; }
|
||||
//! Get if the node has an extra register operand.
|
||||
ASMJIT_INLINE bool hasExtraOp() const noexcept { return !_instDetail.hasExtraReg(); }
|
||||
//! Get extra register operand.
|
||||
ASMJIT_INLINE RegOnly& getExtraReg() noexcept { return _instDetail.extraReg; }
|
||||
//! \overload
|
||||
ASMJIT_INLINE const Operand& getExtraOp() const noexcept { return _extraOp; }
|
||||
//! Set extra operand.
|
||||
ASMJIT_INLINE void setExtraOp(const Operand& extraOp) noexcept { _extraOp = extraOp; }
|
||||
ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _instDetail.extraReg; }
|
||||
//! Set extra register operand to `reg`.
|
||||
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.
|
||||
ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; }
|
||||
@@ -570,11 +571,9 @@ Update:
|
||||
// [Members]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
uint16_t _instId; //!< Instruction id (architecture dependent).
|
||||
Inst::Detail _instDetail; //!< Instruction id, options, and extra register.
|
||||
uint8_t _memOpIndex; //!< \internal
|
||||
uint8_t _reserved; //!< \internal
|
||||
uint32_t _options; //!< Instruction options.
|
||||
Operand _extraOp; //!< Extra operand (REP {cx} or op-mask {k} on AVX-512).
|
||||
uint8_t _reserved[7]; //!< \internal
|
||||
Operand* _opArray; //!< Instruction operands.
|
||||
};
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ CodeEmitter::CodeEmitter(uint32_t type) noexcept
|
||||
_globalHints(0),
|
||||
_globalOptions(kOptionMaybeFailureCase),
|
||||
_options(0),
|
||||
_extraReg(),
|
||||
_inlineComment(nullptr),
|
||||
_extraOp(),
|
||||
_none(),
|
||||
_nativeGpReg(),
|
||||
_nativeGpArray(nullptr) {}
|
||||
@@ -79,9 +79,9 @@ Error CodeEmitter::onDetach(CodeHolder* code) noexcept {
|
||||
_globalOptions = kOptionMaybeFailureCase;
|
||||
|
||||
_options = 0;
|
||||
_extraReg.reset();
|
||||
_inlineComment = nullptr;
|
||||
|
||||
_extraOp.reset();
|
||||
_nativeGpReg.reset();
|
||||
_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
|
||||
|
||||
// ============================================================================
|
||||
// [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
|
||||
|
||||
// [Api-End]
|
||||
|
||||
@@ -315,12 +315,16 @@ public:
|
||||
//! Reset options of the next instruction.
|
||||
ASMJIT_INLINE void resetOptions() noexcept { _options = 0; }
|
||||
|
||||
//! Get if the extra register operand is valid.
|
||||
ASMJIT_INLINE bool hasExtraReg() const noexcept { return _extraReg.isValid(); }
|
||||
//! Get an extra operand that will be used by the next instruction (architecture specific).
|
||||
ASMJIT_INLINE const 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).
|
||||
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).
|
||||
ASMJIT_INLINE void resetExtraOp() noexcept { _extraOp.setSignature(0); }
|
||||
ASMJIT_INLINE void resetExtraReg() noexcept { _extraReg.reset(); }
|
||||
|
||||
//! Get annotation of the next instruction.
|
||||
ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; }
|
||||
@@ -457,13 +461,6 @@ public:
|
||||
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]
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -482,9 +479,10 @@ public:
|
||||
uint32_t _globalHints; //!< Global hints, always in sync with CodeHolder.
|
||||
uint32_t _globalOptions; //!< Global options, combined with `_options` before used by each instruction.
|
||||
|
||||
uint32_t _options; //!< Used to pass instruction options (affects the next instruction).
|
||||
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).
|
||||
uint32_t _options; //!< Used to pass instruction options (affects the next instruction).
|
||||
RegOnly _extraReg; //!< Extra register (op-mask {k} on AVX-512) (affects the next instruction).
|
||||
const char* _inlineComment; //!< Inline comment of the next instruction (affects the next instruction).
|
||||
|
||||
Operand_ _none; //!< Used to pass unused operands to `_emit()` instead of passing null.
|
||||
Reg _nativeGpReg; //!< Native GP register with zero id.
|
||||
const Reg* _nativeGpArray; //!< Array of native registers indexed from zero.
|
||||
|
||||
@@ -548,6 +548,8 @@ ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept {
|
||||
|
||||
x86CallCpuId(®s, 0xD, 1);
|
||||
if (regs.eax & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEOPT);
|
||||
if (regs.eax & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEC);
|
||||
if (regs.eax & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVES);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// [AsmJit]
|
||||
// [AsmJit]
|
||||
// Complete x86/x64 JIT and Remote Assembler for C++.
|
||||
//
|
||||
// [License]
|
||||
@@ -19,6 +19,90 @@ namespace asmjit {
|
||||
//! \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]
|
||||
// ============================================================================
|
||||
@@ -105,8 +189,10 @@ public:
|
||||
kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention).
|
||||
kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention).
|
||||
kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256.
|
||||
kX86FeatureXSAVE, //!< CPU has XSAVE support - XSAVE/XRSTOR, XSETBV/XGETBV, and XCR.
|
||||
kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support - XSAVEOPT/XSAVEOPT64.
|
||||
kX86FeatureXSAVE, //!< CPU has XSAVE support (XSAVE/XRSTOR, XSETBV/XGETBV, and XCR).
|
||||
kX86FeatureXSAVEC, //!< CPU has XSAVEC support (XSAVEC).
|
||||
kX86FeatureXSAVES, //!< CPU has XSAVES support (XSAVES/XRSTORS).
|
||||
kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support (XSAVEOPT/XSAVEOPT64).
|
||||
kX86FeatureOSXSAVE, //!< CPU has XSAVE enabled by OS.
|
||||
kX86FeatureAVX, //!< CPU has AVX.
|
||||
kX86FeatureAVX2, //!< CPU has AVX2.
|
||||
@@ -140,11 +226,6 @@ public:
|
||||
kX86FeaturesCount //!< Count of X86/X64 CPU features.
|
||||
};
|
||||
|
||||
//! \internal
|
||||
enum {
|
||||
kFeaturesPerUInt32 = static_cast<int>(sizeof(uint32_t)) * 8
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [ArmInfo]
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -168,6 +249,7 @@ public:
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE CpuInfo() noexcept { reset(); }
|
||||
ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept { init(other); }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Init / Reset]
|
||||
@@ -178,7 +260,8 @@ public:
|
||||
_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]
|
||||
@@ -197,12 +280,7 @@ public:
|
||||
//! Get CPU architecture sub-type, see \ArchInfo::SubType.
|
||||
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; }
|
||||
//! Get CPU family ID.
|
||||
ASMJIT_INLINE uint32_t getFamily() const noexcept { return _family; }
|
||||
@@ -216,26 +294,17 @@ public:
|
||||
return _hwThreadsCount;
|
||||
}
|
||||
|
||||
//! Get all CPU features.
|
||||
ASMJIT_INLINE const CpuFeatures& getFeatures() const noexcept { return _features; }
|
||||
//! Get whether CPU has a `feature`.
|
||||
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept {
|
||||
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
|
||||
|
||||
uint32_t pos = feature / kFeaturesPerUInt32;
|
||||
uint32_t bit = feature % kFeaturesPerUInt32;
|
||||
|
||||
return static_cast<bool>((_features[pos] >> bit) & 0x1);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { return _features.has(feature); }
|
||||
//! Add a CPU `feature`.
|
||||
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept {
|
||||
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
|
||||
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { _features.add(feature); return *this; }
|
||||
|
||||
uint32_t pos = feature / kFeaturesPerUInt32;
|
||||
uint32_t bit = feature % kFeaturesPerUInt32;
|
||||
|
||||
_features[pos] |= static_cast<uint32_t>(1) << bit;
|
||||
return *this;
|
||||
}
|
||||
//! Get CPU vendor string.
|
||||
ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; }
|
||||
//! Get CPU brand string.
|
||||
ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors - ARM]
|
||||
@@ -277,14 +346,14 @@ public:
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
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 _family; //!< CPU family ID.
|
||||
uint32_t _model; //!< CPU model ID.
|
||||
uint32_t _stepping; //!< CPU stepping.
|
||||
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.
|
||||
union {
|
||||
|
||||
@@ -40,8 +40,6 @@ static const uint64_t kNoBaseAddress = ~static_cast<uint64_t>(0);
|
||||
|
||||
//! Global definitions.
|
||||
ASMJIT_ENUM(Defs) {
|
||||
//! Invalid instruction id.
|
||||
kInvalidInstId = 0,
|
||||
//! Invalid register id.
|
||||
kInvalidRegId = 0xFF,
|
||||
|
||||
@@ -75,23 +73,6 @@ ASMJIT_ENUM(Limits) {
|
||||
|
||||
} // 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]
|
||||
// ============================================================================
|
||||
|
||||
77
src/asmjit/base/inst.cpp
Normal file
77
src/asmjit/base/inst.cpp
Normal 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
108
src/asmjit/base/inst.h
Normal 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
|
||||
@@ -233,17 +233,14 @@ Error Logging::formatInstruction(
|
||||
uint32_t logOptions,
|
||||
const CodeEmitter* emitter,
|
||||
uint32_t archType,
|
||||
uint32_t instId,
|
||||
uint32_t options,
|
||||
const Operand_& extraOp,
|
||||
const Operand_* opArray, uint32_t opCount) noexcept {
|
||||
const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept {
|
||||
|
||||
#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
|
||||
|
||||
#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
|
||||
|
||||
return kErrorInvalidArch;
|
||||
@@ -372,10 +369,7 @@ Error Logging::formatNode(
|
||||
ASMJIT_PROPAGATE(
|
||||
Logging::formatInstruction(sb, logOptions, cb,
|
||||
cb->getArchType(),
|
||||
node->getInstId(),
|
||||
node->getOptions(),
|
||||
node->getExtraOp(),
|
||||
node->getOpArray(), node->getOpCount()));
|
||||
node->getInstDetail(), node->getOpArray(), node->getOpCount()));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -436,10 +430,7 @@ Error Logging::formatNode(
|
||||
ASMJIT_PROPAGATE(
|
||||
Logging::formatInstruction(sb, logOptions, cb,
|
||||
cb->getArchType(),
|
||||
node->getInstId(),
|
||||
node->getOptions(),
|
||||
node->getExtraOp(),
|
||||
node->getOpArray(), node->getOpCount()));
|
||||
node->getInstDetail(), node->getOpArray(), node->getOpCount()));
|
||||
break;
|
||||
}
|
||||
#endif // !ASMJIT_DISABLE_COMPILER
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
#ifndef _ASMJIT_BASE_LOGGING_H
|
||||
#define _ASMJIT_BASE_LOGGING_H
|
||||
|
||||
#include "../asmjit_build.h"
|
||||
|
||||
// [Dependencies]
|
||||
#include "../base/inst.h"
|
||||
#include "../base/string.h"
|
||||
|
||||
// [Api-Begin]
|
||||
@@ -249,10 +248,7 @@ struct Logging {
|
||||
uint32_t logOptions,
|
||||
const CodeEmitter* emitter,
|
||||
uint32_t archType,
|
||||
uint32_t instId,
|
||||
uint32_t options,
|
||||
const Operand_& extraOp,
|
||||
const Operand_* opArray, uint32_t opCount) noexcept;
|
||||
const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept;
|
||||
|
||||
#if !defined(ASMJIT_DISABLE_BUILDER)
|
||||
ASMJIT_API static Error formatNode(
|
||||
@@ -262,7 +258,7 @@ struct Logging {
|
||||
const CBNode* node_) noexcept;
|
||||
#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)
|
||||
enum {
|
||||
// Has to be big to be able to hold all metadata compiler can assign to a
|
||||
|
||||
@@ -709,10 +709,9 @@ public:
|
||||
//! some operands contains a garbage or other metadata in the upper 8 bytes
|
||||
//! then `isSame()` may return `true` in cases where `isEqual()` returns
|
||||
//! 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`.
|
||||
//! Same as `isReg(rType)`, provided for convenience.
|
||||
//! Get if the register type matches `rType` - same as `isReg(rType)`, provided for convenience.
|
||||
ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); }
|
||||
//! Get if the register kind matches `rKind`.
|
||||
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); }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [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]
|
||||
// ============================================================================
|
||||
|
||||
@@ -560,6 +560,7 @@ Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o
|
||||
if (ASMJIT_UNLIKELY(err)) goto Failed;
|
||||
|
||||
cursor = _bufferPtr;
|
||||
options &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,7 +583,7 @@ Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o
|
||||
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;
|
||||
}
|
||||
#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))))
|
||||
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;
|
||||
|
||||
EMIT_BYTE((options & X86Inst::kOptionRepnz) ? 0xF2 : 0xF3);
|
||||
@@ -4041,15 +4042,13 @@ EmitVexEvexR:
|
||||
// Construct `x` - a complete EVEX|VEX prefix.
|
||||
uint32_t x = ((opReg << 4) & 0xF980U) | // [........|........|Vvvvv..R|R.......].
|
||||
((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;
|
||||
|
||||
// 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>());
|
||||
|
||||
if (X86Reg::isK(_extraOp))
|
||||
x |= _extraOp.as<Reg>().getId() << 16; // [@.......|.LL..aaa|Vvvvv..R|RBBmmmmm].
|
||||
|
||||
// Handle AVX512 options by a single branch.
|
||||
const uint32_t kAvx512Options = X86Inst::kOptionZMask |
|
||||
X86Inst::kOption1ToX |
|
||||
@@ -4150,15 +4149,13 @@ EmitVexEvexM:
|
||||
((rxReg << 3 ) & 0x00000040U) | // [........|........|........|.X......].
|
||||
((rxReg << 15) & 0x00080000U) | // [........|....X...|........|........].
|
||||
((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;
|
||||
|
||||
// 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>());
|
||||
|
||||
if (X86Reg::isK(_extraOp))
|
||||
x |= _extraOp.as<Reg>().getId() << 16; // [@.......|.LL.Xaaa|Vvvvv..R|RXBmmmmm].
|
||||
|
||||
// Handle AVX512 options by a single branch.
|
||||
const uint32_t kAvx512Options = X86Inst::kOption1ToX |
|
||||
X86Inst::kOptionZMask |
|
||||
@@ -4483,7 +4480,7 @@ EmitDone:
|
||||
#endif // !ASMJIT_DISABLE_LOGGING
|
||||
|
||||
resetOptions();
|
||||
resetExtraOp();
|
||||
resetExtraReg();
|
||||
resetInlineComment();
|
||||
|
||||
_bufferPtr = cursor;
|
||||
|
||||
@@ -124,13 +124,15 @@ Error X86Compiler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
|
||||
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 !defined(ASMJIT_DISABLE_LOGGING)
|
||||
StringBuilderTmp<256> sb;
|
||||
sb.appendString(DebugUtils::errorAsString(err));
|
||||
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());
|
||||
#else
|
||||
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);
|
||||
|
||||
new(node) CBJump(this, instId, options, opArray, opCount);
|
||||
node->_extraOp = _extraOp;
|
||||
_extraOp.reset();
|
||||
node->_instDetail.extraReg = _extraReg;
|
||||
_extraReg.reset();
|
||||
|
||||
CBLabel* jTarget = nullptr;
|
||||
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);
|
||||
|
||||
node = new(node) CBInst(this, instId, options, opArray, opCount);
|
||||
node->_extraOp = _extraOp;
|
||||
_extraOp.reset();
|
||||
node->_instDetail.extraReg = _extraReg;
|
||||
_extraReg.reset();
|
||||
|
||||
if (inlineComment) {
|
||||
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)
|
||||
};
|
||||
|
||||
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 !defined(ASMJIT_DISABLE_LOGGING)
|
||||
StringBuilderTmp<256> sb;
|
||||
sb.appendString(DebugUtils::errorAsString(err));
|
||||
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());
|
||||
#else
|
||||
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);
|
||||
|
||||
new(node) CBJump(this, instId, options, opArray, opCount);
|
||||
node->_extraOp = _extraOp;
|
||||
_extraOp.reset();
|
||||
node->_instDetail.extraReg = _extraReg;
|
||||
_extraReg.reset();
|
||||
|
||||
CBLabel* jTarget = nullptr;
|
||||
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);
|
||||
|
||||
node = new(node) CBInst(this, instId, options, opArray, opCount);
|
||||
node->_extraOp = _extraOp;
|
||||
_extraOp.reset();
|
||||
node->_instDetail.extraReg = _extraReg;
|
||||
_extraReg.reset();
|
||||
|
||||
if (inlineComment) {
|
||||
inlineComment = static_cast<char*>(_cbDataZone.dup(inlineComment, ::strlen(inlineComment), true));
|
||||
|
||||
@@ -360,12 +360,12 @@ public:
|
||||
|
||||
//! Use REP/REPZ prefix.
|
||||
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);
|
||||
}
|
||||
//! Use REPNZ prefix.
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,8 @@
|
||||
#define _ASMJIT_X86_X86INST_H
|
||||
|
||||
// [Dependencies]
|
||||
#include "../base/assembler.h"
|
||||
#include "../base/assembler.h" // TODO: Is that necessary?
|
||||
#include "../base/inst.h"
|
||||
#include "../base/operand.h"
|
||||
#include "../base/utils.h"
|
||||
#include "../x86/x86globals.h"
|
||||
@@ -301,7 +302,7 @@ struct X86Inst {
|
||||
kIdInsertq, // [ANY] {SSE4A}
|
||||
kIdInt, // [ANY]
|
||||
kIdInt3, // [ANY]
|
||||
kIdInto, // [ANY]
|
||||
kIdInto, // [X86]
|
||||
kIdInvd, // [ANY] {I486}
|
||||
kIdInvlpg, // [ANY] {I486}
|
||||
kIdInvpcid, // [ANY] {I486}
|
||||
@@ -1055,8 +1056,8 @@ struct X86Inst {
|
||||
kIdVmovupd, // [ANY] {AVX|AVX512_F+VL}
|
||||
kIdVmovups, // [ANY] {AVX|AVX512_F+VL}
|
||||
kIdVmpsadbw, // [ANY] {AVX|AVX2}
|
||||
kIdVmulpd, // [ANY] {AVX|AVX2|AVX512_F+VL}
|
||||
kIdVmulps, // [ANY] {AVX|AVX2|AVX512_F+VL}
|
||||
kIdVmulpd, // [ANY] {AVX|AVX512_F+VL}
|
||||
kIdVmulps, // [ANY] {AVX|AVX512_F+VL}
|
||||
kIdVmulsd, // [ANY] {AVX|AVX512_F}
|
||||
kIdVmulss, // [ANY] {AVX|AVX512_F}
|
||||
kIdVorpd, // [ANY] {AVX|AVX512_DQ+VL}
|
||||
@@ -1426,8 +1427,8 @@ struct X86Inst {
|
||||
kIdVsqrtsd, // [ANY] {AVX|AVX512_F}
|
||||
kIdVsqrtss, // [ANY] {AVX|AVX512_F}
|
||||
kIdVstmxcsr, // [ANY] {AVX}
|
||||
kIdVsubpd, // [ANY] {AVX|AVX2|AVX512_F+VL}
|
||||
kIdVsubps, // [ANY] {AVX|AVX2|AVX512_F+VL}
|
||||
kIdVsubpd, // [ANY] {AVX|AVX512_F+VL}
|
||||
kIdVsubps, // [ANY] {AVX|AVX512_F+VL}
|
||||
kIdVsubsd, // [ANY] {AVX|AVX512_F}
|
||||
kIdVsubss, // [ANY] {AVX|AVX512_F}
|
||||
kIdVtestpd, // [ANY] {AVX}
|
||||
@@ -1458,16 +1459,16 @@ struct X86Inst {
|
||||
kIdXorps, // [ANY] {SSE}
|
||||
kIdXrstor, // [ANY] {XSAVE}
|
||||
kIdXrstor64, // [X64] {XSAVE}
|
||||
kIdXrstors, // [ANY] {XSAVE}
|
||||
kIdXrstors64, // [X64] {XSAVE}
|
||||
kIdXrstors, // [ANY] {XSAVES}
|
||||
kIdXrstors64, // [X64] {XSAVES}
|
||||
kIdXsave, // [ANY] {XSAVE}
|
||||
kIdXsave64, // [X64] {XSAVE}
|
||||
kIdXsavec, // [ANY] {XSAVE}
|
||||
kIdXsavec64, // [X64] {XSAVE}
|
||||
kIdXsavec, // [ANY] {XSAVEC}
|
||||
kIdXsavec64, // [X64] {XSAVEC}
|
||||
kIdXsaveopt, // [ANY] {XSAVEOPT}
|
||||
kIdXsaveopt64, // [X64] {XSAVEOPT}
|
||||
kIdXsaves, // [ANY] {XSAVE}
|
||||
kIdXsaves64, // [X64] {XSAVE}
|
||||
kIdXsaves, // [ANY] {XSAVES}
|
||||
kIdXsaves64, // [X64] {XSAVES}
|
||||
kIdXsetbv, // [ANY] {XSAVE}
|
||||
kIdXtest, // [ANY] {TSX}
|
||||
_kIdCount
|
||||
@@ -1996,13 +1997,13 @@ struct X86Inst {
|
||||
|
||||
kOptionER = 0x00040000U, //!< AVX-512: 'embedded-rounding' {er} and {sae}.
|
||||
kOptionSAE = 0x00080000U, //!< AVX-512: 'suppress-all-exceptions' {sae}.
|
||||
|
||||
kOption1ToX = 0x00100000U, //!< AVX-512: broadcast the first element to all {1tox}.
|
||||
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).
|
||||
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).
|
||||
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).
|
||||
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); }
|
||||
//! Get if the instruction is MMX|3DNOW instruction that accesses MMX registers (includes EMMS).
|
||||
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).
|
||||
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.
|
||||
ASMJIT_INLINE bool isLockEnabled() const noexcept { return hasFlag(kFlagLock); }
|
||||
@@ -2167,6 +2173,8 @@ struct X86Inst {
|
||||
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).
|
||||
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}.
|
||||
ASMJIT_INLINE bool hasAvx512K() const noexcept { return hasFlag(kFlagAvx512K); }
|
||||
@@ -2184,7 +2192,7 @@ struct X86Inst {
|
||||
ASMJIT_INLINE bool hasAvx512B64() const noexcept { return hasFlag(kFlagAvx512B64); }
|
||||
|
||||
//! 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.
|
||||
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 _iSignatureIndex :10; //!< First `ISignature` entry in the database.
|
||||
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 _reserved : 5; //!< \internal
|
||||
};
|
||||
@@ -2326,8 +2334,13 @@ struct X86Inst {
|
||||
ASMJIT_INLINE bool isFpu() const noexcept { return getCommonData().isFpu(); }
|
||||
//! Get if the instruction is MMX instruction that accesses MMX registersm, including EMMS.
|
||||
ASMJIT_INLINE bool isMmx() const noexcept { return getCommonData().isMmx(); }
|
||||
|
||||
//! Get if the instruction is SSE|AVX|AVX512 instruction that accesses XMM|YMM|ZMM registers.
|
||||
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.
|
||||
ASMJIT_INLINE bool isLockEnabled() const noexcept { return getCommonData().isLockEnabled(); }
|
||||
@@ -2372,7 +2385,7 @@ struct X86Inst {
|
||||
// [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; }
|
||||
|
||||
//! Get instruction information based on the instruction `instId`.
|
||||
@@ -2442,17 +2455,6 @@ struct X86Inst {
|
||||
ASMJIT_API static const char* getNameById(uint32_t instId) noexcept;
|
||||
#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]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
731
src/asmjit/x86/x86instimpl.cpp
Normal file
731
src/asmjit/x86/x86instimpl.cpp
Normal 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"
|
||||
45
src/asmjit/x86/x86instimpl_p.h
Normal file
45
src/asmjit/x86/x86instimpl_p.h
Normal 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
|
||||
@@ -725,7 +725,7 @@ ASMJIT_FAVOR_SIZE Error X86Internal::emitRegMove(X86Emitter* emitter,
|
||||
Operand dst(dst_);
|
||||
Operand src(src_);
|
||||
|
||||
uint32_t instId = Globals::kInvalidInstId;
|
||||
uint32_t instId = Inst::kIdNone;
|
||||
uint32_t memFlags = 0;
|
||||
|
||||
enum MemFlags {
|
||||
@@ -831,7 +831,7 @@ ASMJIT_FAVOR_SIZE Error X86Internal::emitArgMove(X86Emitter* emitter,
|
||||
uint32_t dstSize = TypeId::sizeOf(dstTypeId);
|
||||
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'.
|
||||
for (;;) {
|
||||
|
||||
@@ -580,10 +580,10 @@ ASMJIT_FAVOR_SIZE Error X86Logging::formatInstruction(
|
||||
uint32_t logOptions,
|
||||
const CodeEmitter* emitter,
|
||||
uint32_t archType,
|
||||
uint32_t instId,
|
||||
uint32_t options,
|
||||
const Operand_& extraOp,
|
||||
const Operand_* opArray, uint32_t opCount) noexcept {
|
||||
const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept {
|
||||
|
||||
uint32_t instId = detail.instId;
|
||||
uint32_t options = detail.options;
|
||||
|
||||
// Format instruction options and instruction mnemonic.
|
||||
if (instId < X86Inst::_kIdCount) {
|
||||
@@ -601,9 +601,9 @@ ASMJIT_FAVOR_SIZE Error X86Logging::formatInstruction(
|
||||
// REP|REPNZ options.
|
||||
if (options & (X86Inst::kOptionRep | X86Inst::kOptionRepnz)) {
|
||||
sb.appendString((options & X86Inst::kOptionRep) ? "rep " : "repnz ");
|
||||
if (!extraOp.isNone()) {
|
||||
if (detail.hasExtraReg()) {
|
||||
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("} "));
|
||||
}
|
||||
}
|
||||
@@ -654,9 +654,9 @@ ASMJIT_FAVOR_SIZE Error X86Logging::formatInstruction(
|
||||
|
||||
// Support AVX-512 {k}{z}.
|
||||
if (i == 0) {
|
||||
if (X86Reg::isK(extraOp)) {
|
||||
if (detail.extraReg.getKind() == X86Reg::kKindK) {
|
||||
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('}'));
|
||||
|
||||
if (options & X86Inst::kOptionZMask)
|
||||
|
||||
@@ -48,10 +48,7 @@ struct X86Logging {
|
||||
uint32_t logOptions,
|
||||
const CodeEmitter* emitter,
|
||||
uint32_t archType,
|
||||
uint32_t instId,
|
||||
uint32_t options,
|
||||
const Operand_& extraOp,
|
||||
const Operand_* opArray, uint32_t opCount) noexcept;
|
||||
const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept;
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
@@ -1723,9 +1723,9 @@ _NextGroup:
|
||||
_avxEnabled = true;
|
||||
}
|
||||
|
||||
const Operand_& extraOp = node->getExtraOp();
|
||||
if (extraOp.isReg()) {
|
||||
uint32_t id = extraOp.as<Reg>().getId();
|
||||
const RegOnly& extraReg = node->getExtraReg();
|
||||
if (extraReg.isValid()) {
|
||||
uint32_t id = extraReg.getId();
|
||||
if (cc()->isVirtRegValid(id)) {
|
||||
VirtReg* vreg = cc()->getVirtRegById(id);
|
||||
TiedReg* tied;
|
||||
@@ -2117,10 +2117,7 @@ Error X86RAPass::annotate() {
|
||||
0,
|
||||
cc(),
|
||||
cc()->getArchType(),
|
||||
node->getInstId(),
|
||||
node->getOptions(),
|
||||
node->getExtraOp(),
|
||||
node->getOpArray(), node->getOpCount());
|
||||
node->getInstDetail(), node->getOpArray(), node->getOpCount());
|
||||
|
||||
node_->setInlineComment(
|
||||
static_cast<char*>(dataZone.dup(sb.getData(), sb.getLength(), true)));
|
||||
@@ -2423,8 +2420,11 @@ Error X86VarAlloc::run(CBNode* node_) {
|
||||
// Translate node operands.
|
||||
if (node_->getType() == CBNode::kNodeInst) {
|
||||
CBInst* node = static_cast<CBInst*>(node_);
|
||||
if (node->hasExtraOp())
|
||||
ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, &node->_extraOp, 1));
|
||||
if (node->hasExtraOp()) {
|
||||
Reg reg = node->getExtraReg().toReg<Reg>();
|
||||
ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, ®, 1));
|
||||
node->setExtraReg(reg);
|
||||
}
|
||||
ASMJIT_PROPAGATE(X86RAPass_translateOperands(_context, node->getOpArray(), node->getOpCount()));
|
||||
}
|
||||
else if (node_->getType() == CBNode::kNodePushArg) {
|
||||
|
||||
@@ -114,6 +114,8 @@ static void dumpCpu(void) {
|
||||
{ CpuInfo::kX86FeatureSMEP , "SMEP" },
|
||||
{ CpuInfo::kX86FeatureSHA , "SHA" },
|
||||
{ CpuInfo::kX86FeatureXSAVE , "XSAVE" },
|
||||
{ CpuInfo::kX86FeatureXSAVEC , "XSAVEC" },
|
||||
{ CpuInfo::kX86FeatureXSAVES , "XSAVES" },
|
||||
{ CpuInfo::kX86FeatureXSAVEOPT , "XSAVEOPT" },
|
||||
{ CpuInfo::kX86FeatureOSXSAVE , "OSXSAVE" },
|
||||
{ CpuInfo::kX86FeatureAVX , "AVX" },
|
||||
|
||||
@@ -1639,7 +1639,7 @@ class X86Generator extends base.BaseGenerator {
|
||||
}
|
||||
|
||||
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` +
|
||||
`const X86Inst::CommonData X86InstDB::commonData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
|
||||
`#undef SINGLE_REG\n` +
|
||||
|
||||
Reference in New Issue
Block a user