mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-18 13:04:36 +03:00
Fixed Issue #24
Added move information to instruction database that the Compiler can use to make the read/write information of each operand more precise.
This commit is contained in:
@@ -2182,27 +2182,46 @@ _NextGroup:
|
||||
uint32_t combinedFlags;
|
||||
|
||||
if (i == 0) {
|
||||
// Default for the first operand.
|
||||
// Read/Write is usualy the combination of the first operand.
|
||||
combinedFlags = inFlags | outFlags;
|
||||
|
||||
// Comparison/Test instructions never modify the source operand.
|
||||
if (info->isTest()) {
|
||||
combinedFlags = inFlags;
|
||||
}
|
||||
// Move instructions typically overwrite the first operand, but
|
||||
// there are some exceptions based on the operands' size and type.
|
||||
else if (info->isMove()) {
|
||||
// Cvttsd2si/Cvttss2si. In 32-bit mode the whole destination is replaced.
|
||||
// In 64-bit mode we need to check whether the destination operand size
|
||||
// is 64-bits.
|
||||
if (code == kInstCvttsd2si || code == kInstCvttss2si)
|
||||
combinedFlags = vd->getSize() > 4 ? (op->isRegType(kRegTypeGpq) ? outFlags : inFlags | outFlags) : outFlags;
|
||||
// Movss/Movsd. These instructions won't overwrite the whole register if move
|
||||
// is between two registers.
|
||||
else if (code == kInstMovss || code == kInstMovsd)
|
||||
combinedFlags = opList[1].isMem() ? outFlags : inFlags | outFlags;
|
||||
else
|
||||
// Move instructions typically overwrite the first operand,
|
||||
// but there are some exceptions based on the operands' size
|
||||
// and type.
|
||||
if (info->isMove()) {
|
||||
uint32_t movSize = info->getMoveSize();
|
||||
uint32_t varSize = vd->getSize();
|
||||
|
||||
// Exception - If the source operand is a memory location
|
||||
// promote move size into 16 bytes.
|
||||
if (info->isZeroIfMem() && opList[1].isMem())
|
||||
movSize = 16;
|
||||
|
||||
if (movSize >= varSize) {
|
||||
// If move size is greater than or equal to the size of
|
||||
// the variable there is nothing to do, because the move
|
||||
// will overwrite the variable in all cases.
|
||||
combinedFlags = outFlags;
|
||||
}
|
||||
else if (static_cast<const X86Var*>(op)->isGp()) {
|
||||
uint32_t opSize = static_cast<const X86Var*>(op)->getSize();
|
||||
|
||||
// Move size is zero in case that it should be determined
|
||||
// from the destination register.
|
||||
if (movSize == 0)
|
||||
movSize = opSize;
|
||||
|
||||
// Handle the case that a 32-bit operation in 64-bit mode
|
||||
// always zeroes the rest of the destination register and
|
||||
// the case that move size is actually greater than or
|
||||
// equal to the size of the variable.
|
||||
if (movSize >= 4 || movSize >= varSize)
|
||||
combinedFlags = outFlags;
|
||||
}
|
||||
}
|
||||
// Comparison/Test instructions don't modify any operand.
|
||||
else if (info->isTest()) {
|
||||
combinedFlags = inFlags;
|
||||
}
|
||||
// Imul.
|
||||
else if (code == kInstImul && opCount == 3) {
|
||||
@@ -2210,10 +2229,13 @@ _NextGroup:
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default for secon/third operands.
|
||||
// Read-Only is usualy the combination of the second/third/fourth operands.
|
||||
combinedFlags = inFlags;
|
||||
|
||||
// Xchg/Xadd/Imul/Idiv.
|
||||
// Idiv is a special instruction, never handled here.
|
||||
ASMJIT_ASSERT(code != kInstIdiv);
|
||||
|
||||
// Xchg/Xadd/Imul.
|
||||
if (info->isXchg() || (code == kInstImul && opCount == 3 && i == 1))
|
||||
combinedFlags = inFlags | outFlags;
|
||||
}
|
||||
@@ -2240,26 +2262,26 @@ _NextGroup:
|
||||
// Default for the first operand.
|
||||
combinedFlags = inFlags | outFlags;
|
||||
|
||||
// Comparison/Test instructions never modify the source operand.
|
||||
if (info->isTest()) {
|
||||
combinedFlags = inFlags;
|
||||
}
|
||||
// Move instructions typically overwrite the first operand, but
|
||||
// there are some exceptions based on the operands' size and type.
|
||||
else if (info->isMove()) {
|
||||
// Movss.
|
||||
if (code == kInstMovss)
|
||||
combinedFlags = vd->getSize() == 4 ? outFlags : inFlags | outFlags;
|
||||
// Movsd.
|
||||
else if (code == kInstMovsd)
|
||||
combinedFlags = vd->getSize() == 8 ? outFlags : inFlags | outFlags;
|
||||
else
|
||||
// Move to memory - setting the right flags is important
|
||||
// as if it's just move to the register. It's just a bit
|
||||
// simpler as there are no special cases.
|
||||
if (info->isMove()) {
|
||||
uint32_t movSize = IntUtil::iMax<uint32_t>(info->getMoveSize(), m->getSize());
|
||||
uint32_t varSize = vd->getSize();
|
||||
|
||||
if (movSize >= varSize)
|
||||
combinedFlags = outFlags;
|
||||
}
|
||||
// Comparison/Test instructions don't modify any operand.
|
||||
else if (info->isTest()) {
|
||||
combinedFlags = inFlags;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default for the second operand.
|
||||
combinedFlags = inFlags;
|
||||
|
||||
// Handle Xchg instruction (modifies both operands).
|
||||
if (info->isXchg())
|
||||
combinedFlags = inFlags | outFlags;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1698,17 +1698,21 @@ ASMJIT_ENUM(kInstFlags) {
|
||||
//!
|
||||
//! Move instructions typically overwrite the first operand by the second
|
||||
//! operand. The first operand can be the exact copy of the second operand
|
||||
//! or it can be any kind of conversion. Mov instructions are typically
|
||||
//! 'mov', 'movd', 'movq', 'movdq?', 'cmov??' like instructions, but we also
|
||||
//! consider 'lea' (Load Effective Address), multimedia instructions like
|
||||
//! 'cvtdq2pd', shuffle instructions like 'pshufb' and SSE/SSE2 mathematic
|
||||
//! instructions like 'rcp??', 'round??' and 'rsqrt??'.
|
||||
//! or it can be any kind of conversion or shuffling.
|
||||
//!
|
||||
//! Mov instructions are 'mov', 'movd', 'movq', movdq', 'lea', multimedia
|
||||
//! instructions like 'cvtdq2pd', shuffle instructions like 'pshufb' and
|
||||
//! SSE/SSE2 mathematic instructions like 'rcp?', 'round?' and 'rsqrt?'.
|
||||
//!
|
||||
//! There are some MOV instructions that do only a partial move (for example
|
||||
//! 'cvtsi2ss'), register allocator has to know the variable size and use
|
||||
//! the flag accordingly to it.
|
||||
kInstFlagMove = 0x0004,
|
||||
|
||||
//! @brief Instruction is an exchange like instruction.
|
||||
//!
|
||||
//! Exchange instruction typically overwrite first and second operand, we
|
||||
//! count 'xchg' and 'xadd' instructions right now.
|
||||
//! Exchange instruction typically overwrite first and second operand. So
|
||||
//! far only the instructions 'xchg' and 'xadd' are considered.
|
||||
kInstFlagXchg = 0x0008,
|
||||
|
||||
//! @brief Instruction accesses Fp register(s).
|
||||
@@ -1745,6 +1749,9 @@ ASMJIT_ENUM(kInstFlags) {
|
||||
//! @brief Combination of @c kInstFlagMem4 and @c kInstFlagMem8 and @c kInstFlagMem10.
|
||||
kInstFlagMem4_8_10 = kInstFlagMem4_8 | kInstFlagMem10,
|
||||
|
||||
//! @brief Zeroes the rest of the register if the source operand is memory.
|
||||
kInstFlagZeroIfMem = 0x1000,
|
||||
|
||||
//! @brief REX.W/VEX.W by default.
|
||||
kInstFlagW = 0x8000
|
||||
};
|
||||
@@ -1938,54 +1945,98 @@ struct InstInfo {
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! @brief Get instruction name string (null terminated string).
|
||||
ASMJIT_INLINE const char* getName() const { return _instName + static_cast<uint32_t>(_nameIndex); }
|
||||
ASMJIT_INLINE const char* getName() const {
|
||||
return _instName + static_cast<uint32_t>(_nameIndex);
|
||||
}
|
||||
|
||||
//! @brief Get instruction name index (index to @ref _instName array).
|
||||
ASMJIT_INLINE uint32_t _getNameIndex() const { return _nameIndex; }
|
||||
ASMJIT_INLINE uint32_t _getNameIndex() const {
|
||||
return _nameIndex;
|
||||
}
|
||||
|
||||
//! @brief Get instruction group, see @ref kInstGroup.
|
||||
ASMJIT_INLINE uint32_t getGroup() const { return _group; }
|
||||
ASMJIT_INLINE uint32_t getGroup() const {
|
||||
return _group;
|
||||
}
|
||||
|
||||
//! @brief Get size of move instruction in bytes.
|
||||
//!
|
||||
//! If zero, the size of MOV instruction is determined by the size of the
|
||||
//! destination register (applies mostly for x86 arithmetic). This value is
|
||||
//! useful for register allocator when determining if a variable is going to
|
||||
//! be overwritten or not. Basically if the move size is equal or greater
|
||||
//! than a variable itself it is considered overwritten.
|
||||
ASMJIT_INLINE uint32_t getMoveSize() const {
|
||||
return _moveSize;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Flags]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! @brief Get instruction flags, see @ref kInstFlags.
|
||||
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
|
||||
ASMJIT_INLINE uint32_t getFlags() const {
|
||||
return _flags;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction is a control-flow intruction.
|
||||
//!
|
||||
//! Control flow instruction is instruction that modifies instruction pointer,
|
||||
//! typically jmp, jcc, call, or ret.
|
||||
ASMJIT_INLINE bool isFlow() const { return (_flags & kInstFlagFlow) != 0; }
|
||||
ASMJIT_INLINE bool isFlow() const {
|
||||
return (_flags & kInstFlagFlow) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction is a compare/test like intruction.
|
||||
ASMJIT_INLINE bool isTest() const { return (_flags & kInstFlagTest) != 0; }
|
||||
ASMJIT_INLINE bool isTest() const {
|
||||
return (_flags & kInstFlagTest) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction is a typical Move instruction.
|
||||
//!
|
||||
//! Move instructions typically overwrite the first operand, so it's an useful
|
||||
//! hint for @ref Compiler. Applies also to multimedia instruction - MMX,
|
||||
//! SSE, SSE2 and AVX moves).
|
||||
ASMJIT_INLINE bool isMove() const { return (_flags & kInstFlagMove) != 0; }
|
||||
ASMJIT_INLINE bool isMove() const {
|
||||
return (_flags & kInstFlagMove) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction is a typical Exchange instruction.
|
||||
//!
|
||||
//! Exchange instructios are 'xchg' and 'xadd'.
|
||||
ASMJIT_INLINE bool isXchg() const { return (_flags & kInstFlagXchg) != 0; }
|
||||
ASMJIT_INLINE bool isXchg() const {
|
||||
return (_flags & kInstFlagXchg) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction accesses Fp register(s).
|
||||
ASMJIT_INLINE bool isFp() const { return (_flags & kInstFlagFp) != 0; }
|
||||
ASMJIT_INLINE bool isFp() const {
|
||||
return (_flags & kInstFlagFp) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction can be prefixed by LOCK prefix.
|
||||
ASMJIT_INLINE bool isLockable() const { return (_flags & kInstFlagLock) != 0; }
|
||||
ASMJIT_INLINE bool isLockable() const {
|
||||
return (_flags & kInstFlagLock) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction is special type (this is used by
|
||||
//! @c Compiler to manage additional variables or functionality).
|
||||
ASMJIT_INLINE bool isSpecial() const { return (_flags & kInstFlagSpecial) != 0; }
|
||||
ASMJIT_INLINE bool isSpecial() const {
|
||||
return (_flags & kInstFlagSpecial) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the instruction is special type and it performs
|
||||
//! memory access.
|
||||
ASMJIT_INLINE bool isSpecialMem() const { return (_flags & kInstFlagSpecialMem) != 0; }
|
||||
ASMJIT_INLINE bool isSpecialMem() const {
|
||||
return (_flags & kInstFlagSpecialMem) != 0;
|
||||
}
|
||||
|
||||
//! @brief Get whether the move instruction zeroes the rest of the register
|
||||
//! if the source is memory operand.
|
||||
//!
|
||||
//! Basically flag needed by 'movsd' and 'movss' instructions.
|
||||
ASMJIT_INLINE bool isZeroIfMem() const {
|
||||
return (_flags & kInstFlagZeroIfMem) != 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
@@ -1997,8 +2048,13 @@ struct InstInfo {
|
||||
uint16_t _flags;
|
||||
//! @brief Instruction group, used also by @c Compiler.
|
||||
uint8_t _group;
|
||||
//! @brief Count of bytes overritten by a move instruction.
|
||||
//!
|
||||
//! Only used when kInstFlagMove flag is set. If this value is zero move
|
||||
//! depends on the destination register size.
|
||||
uint8_t _moveSize;
|
||||
//! @brief Reserved for future use.
|
||||
uint8_t _reserved[3];
|
||||
uint8_t _reserved[2];
|
||||
//! @brief Operands' flags.
|
||||
uint16_t _opFlags[4];
|
||||
//! @brief Primary and secondary opcodes.
|
||||
@@ -2129,82 +2185,97 @@ struct RegMask {
|
||||
// [Zero]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void zero(uint32_t c)
|
||||
{ _packed.u16[c] = 0; }
|
||||
ASMJIT_INLINE void zero(uint32_t c) {
|
||||
_packed.u16[c] = 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Get]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE uint32_t get(uint32_t c) const
|
||||
{ return _packed.u16[c]; }
|
||||
ASMJIT_INLINE uint32_t get(uint32_t c) const {
|
||||
return _packed.u16[c];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Set]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void set(uint32_t c, uint32_t mask)
|
||||
{ _packed.u16[c] = static_cast<uint16_t>(mask); }
|
||||
ASMJIT_INLINE void set(uint32_t c, uint32_t mask) {
|
||||
_packed.u16[c] = static_cast<uint16_t>(mask);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void set(const RegMask& other)
|
||||
{ _packed.setUInt64(other._packed); }
|
||||
ASMJIT_INLINE void set(const RegMask& other) {
|
||||
_packed.setUInt64(other._packed);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Add]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void add(uint32_t c, uint32_t mask)
|
||||
{ _packed.u16[c] |= static_cast<uint16_t>(mask); }
|
||||
ASMJIT_INLINE void add(uint32_t c, uint32_t mask) {
|
||||
_packed.u16[c] |= static_cast<uint16_t>(mask);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void add(const RegMask& other)
|
||||
{ _packed.or_(other._packed); }
|
||||
ASMJIT_INLINE void add(const RegMask& other) {
|
||||
_packed.or_(other._packed);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Del]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void del(uint32_t c, uint32_t mask)
|
||||
{ _packed.u16[c] &= ~static_cast<uint16_t>(mask); }
|
||||
ASMJIT_INLINE void del(uint32_t c, uint32_t mask) {
|
||||
_packed.u16[c] &= ~static_cast<uint16_t>(mask);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void del(const RegMask& other)
|
||||
{ _packed.del(other._packed); }
|
||||
ASMJIT_INLINE void del(const RegMask& other) {
|
||||
_packed.del(other._packed);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [And]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void and_(uint32_t c, uint32_t mask)
|
||||
{ _packed.u16[c] &= static_cast<uint16_t>(mask); }
|
||||
ASMJIT_INLINE void and_(uint32_t c, uint32_t mask) {
|
||||
_packed.u16[c] &= static_cast<uint16_t>(mask);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void and_(const RegMask& other)
|
||||
{ _packed.and_(other._packed); }
|
||||
ASMJIT_INLINE void and_(const RegMask& other) {
|
||||
_packed.and_(other._packed);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Xor]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void xor_(uint32_t c, uint32_t mask)
|
||||
{ _packed.u16[c] ^= static_cast<uint16_t>(mask); }
|
||||
ASMJIT_INLINE void xor_(uint32_t c, uint32_t mask) {
|
||||
_packed.u16[c] ^= static_cast<uint16_t>(mask);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void xor_(const RegMask& other)
|
||||
{ _packed.xor_(other._packed); }
|
||||
ASMJIT_INLINE void xor_(const RegMask& other) {
|
||||
_packed.xor_(other._packed);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [IsEmpty / Has]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE bool isEmpty() const
|
||||
{ return _packed.isZero(); }
|
||||
ASMJIT_INLINE bool isEmpty() const {
|
||||
return _packed.isZero();
|
||||
}
|
||||
|
||||
ASMJIT_INLINE bool has(uint32_t c, uint32_t mask = 0xFFFFFFFF) const
|
||||
{ return (static_cast<uint32_t>(_packed.u16[c]) & mask) != 0; }
|
||||
ASMJIT_INLINE bool has(uint32_t c, uint32_t mask = 0xFFFFFFFF) const {
|
||||
return (static_cast<uint32_t>(_packed.u16[c]) & mask) != 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void reset()
|
||||
{ _packed.reset(); }
|
||||
ASMJIT_INLINE void reset() {
|
||||
_packed.reset();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
|
||||
Reference in New Issue
Block a user