[VM] Make x64 assembler more regular

Apart from removing almost 1000 lines of very repetitive code, the idea here is
to change the assembler from a huge pile of arbitrary code into something that
can be used to generate tables of opcodes and their structure. Later, I'd like to
use this to make the disassembler more table driven and less arbitrary, and
perhaps build an x86 simulator in the same vein as the ARM simulator, which
would help me debug (I find the ARM simulator very useful when making low level
changes to the VM and miss its functionality on x86).

R=vegorov@google.com

Bug:
Change-Id: I1ae2c1696f88b67862843c9ac05c827a7c9b9a6e
Reviewed-on: https://dart-review.googlesource.com/25241
Commit-Queue: Erik Corry <erikcorry@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
Erik Corry 2017-12-12 14:14:45 +00:00 committed by commit-bot@chromium.org
parent b1fce8078e
commit 1dec18e2cf
10 changed files with 829 additions and 1827 deletions

View file

@ -12,6 +12,7 @@
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/constants_ia32.h"
#include "vm/constants_x86.h"
namespace dart {

View file

@ -1492,7 +1492,7 @@ ASSEMBLER_TEST_RUN(PackedCompareEQ, test) {
"mov eax,0x........\n"
"movd xmm1,eax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [0]\n"
"cmpps xmm0,xmm1 [eq]\n"
"push eax\n"
"movss [esp],xmm0\n"
"fld_s [esp]\n"
@ -1523,7 +1523,7 @@ ASSEMBLER_TEST_RUN(PackedCompareNEQ, test) {
"mov eax,0x........\n"
"movd xmm1,eax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [4]\n"
"cmpps xmm0,xmm1 [neq]\n"
"push eax\n"
"movss [esp],xmm0\n"
"fld_s [esp]\n"
@ -1554,7 +1554,7 @@ ASSEMBLER_TEST_RUN(PackedCompareLT, test) {
"mov eax,0x........\n"
"movd xmm1,eax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [1]\n"
"cmpps xmm0,xmm1 [lt]\n"
"push eax\n"
"movss [esp],xmm0\n"
"fld_s [esp]\n"
@ -1585,7 +1585,7 @@ ASSEMBLER_TEST_RUN(PackedCompareLE, test) {
"mov eax,0x........\n"
"movd xmm1,eax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [2]\n"
"cmpps xmm0,xmm1 [le]\n"
"push eax\n"
"movss [esp],xmm0\n"
"fld_s [esp]\n"
@ -1616,7 +1616,7 @@ ASSEMBLER_TEST_RUN(PackedCompareNLT, test) {
"mov eax,0x........\n"
"movd xmm1,eax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [5]\n"
"cmpps xmm0,xmm1 [nlt]\n"
"push eax\n"
"movss [esp],xmm0\n"
"fld_s [esp]\n"
@ -1647,7 +1647,7 @@ ASSEMBLER_TEST_RUN(PackedCompareNLE, test) {
"mov eax,0x........\n"
"movd xmm1,eax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [6]\n"
"cmpps xmm0,xmm1 [nle]\n"
"push eax\n"
"movss [esp],xmm0\n"
"fld_s [esp]\n"
@ -2632,7 +2632,7 @@ ASSEMBLER_TEST_RUN(PackedSingleToDouble, test) {
EXPECT_FLOAT_EQ(9.0f, res, 0.000001f);
EXPECT_DISASSEMBLY(
"movups xmm1,[rip+0x.......]\n"
"cvtsd2ss xmm0,xmm1\n"
"cvtps2pd xmm0,xmm1\n"
"push eax\n"
"push eax\n"
"movsd [esp],xmm0\n"
@ -3277,7 +3277,7 @@ ASSEMBLER_TEST_RUN(DoubleToFloatConversion, test) {
"mov eax,0x........\n"
"push eax\n"
"movsd xmm0,[esp]\n"
"(null) xmm1,xmm0\n"
"cvtsd2ss xmm1,xmm0\n"
"movss [esp],xmm1\n"
"fld_s [esp]\n"
"pop eax\n"
@ -3531,6 +3531,37 @@ ASSEMBLER_TEST_RUN(SquareRootDouble, test) {
"ret\n");
}
ASSEMBLER_TEST_GENERATE(XmmAlu, assembler) {
// Test the disassembler.
__ addss(XMM0, XMM0);
__ addsd(XMM0, XMM0);
__ addps(XMM0, XMM0);
__ addpd(XMM0, XMM0);
__ cvtss2sd(XMM0, XMM0);
__ cvtsd2ss(XMM0, XMM0);
__ cvtps2pd(XMM0, XMM0);
__ cvtpd2ps(XMM0, XMM0);
__ movl(EAX, Immediate(0));
__ ret();
}
ASSEMBLER_TEST_RUN(XmmAlu, test) {
typedef intptr_t (*XmmAluTest)();
intptr_t res = reinterpret_cast<XmmAluTest>(test->entry())();
EXPECT_EQ(res, 0);
EXPECT_DISASSEMBLY(
"addss xmm0,xmm0\n"
"addsd xmm0,xmm0\n"
"addps xmm0,xmm0\n"
"addpd xmm0,xmm0\n"
"cvtss2sd xmm0,xmm0\n"
"cvtsd2ss xmm0,xmm0\n"
"cvtps2pd xmm0,xmm0\n"
"cvtpd2ps xmm0,xmm0\n"
"mov eax,0\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(FloatNegate, assembler) {
__ movss(XMM0, Address(ESP, kWordSize));
__ FloatNegate(XMM0);
@ -4616,7 +4647,7 @@ ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) {
"mov esi,[esp+0x10]\n"
"mov edi,[esp+0x14]\n"
"mov ecx,[esp+0x18]\n"
"rep movs\n"
"rep movsb\n"
"pop ecx\n"
"pop edi\n"
"pop esi\n"

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/constants_x64.h"
#include "vm/constants_x86.h"
#include "vm/hash_map.h"
#include "vm/object.h"
@ -31,6 +32,7 @@ class Immediate : public ValueObject {
bool is_int8() const { return Utils::IsInt(8, value_); }
bool is_uint8() const { return Utils::IsUint(8, value_); }
bool is_int16() const { return Utils::IsInt(16, value_); }
bool is_uint16() const { return Utils::IsUint(16, value_); }
bool is_int32() const { return Utils::IsInt(32, value_); }
bool is_uint32() const { return Utils::IsUint(32, value_); }
@ -353,147 +355,222 @@ class Assembler : public ValueObject {
/*
* Emit Machine Instructions.
*/
void call(Register reg);
void call(const Address& address);
void call(Register reg) { EmitUnaryL(reg, 0xFF, 2); }
void call(const Address& address) { EmitUnaryL(address, 0xFF, 2); }
void call(Label* label);
void call(const ExternalLabel* label);
static const intptr_t kCallExternalLabelSize = 15;
void pushq(Register reg);
void pushq(const Address& address);
void pushq(const Address& address) { EmitUnaryL(address, 0xFF, 6); }
void pushq(const Immediate& imm);
void PushImmediate(const Immediate& imm);
void popq(Register reg);
void popq(const Address& address);
void popq(const Address& address) { EmitUnaryL(address, 0x8F, 0); }
void setcc(Condition condition, ByteRegister dst);
void movl(Register dst, Register src);
// Register-register, register-address and address-register instructions.
#define RR(width, name, ...) \
void name(Register dst, Register src) { Emit##width(dst, src, __VA_ARGS__); }
#define RA(width, name, ...) \
void name(Register dst, const Address& src) { \
Emit##width(dst, src, __VA_ARGS__); \
}
#define AR(width, name, ...) \
void name(const Address& dst, Register src) { \
Emit##width(src, dst, __VA_ARGS__); \
}
#define REGULAR_INSTRUCTION(name, ...) \
RA(W, name##w, __VA_ARGS__) \
RA(L, name##l, __VA_ARGS__) \
RA(Q, name##q, __VA_ARGS__) \
RR(W, name##w, __VA_ARGS__) \
RR(L, name##l, __VA_ARGS__) \
RR(Q, name##q, __VA_ARGS__)
REGULAR_INSTRUCTION(test, 0x85)
REGULAR_INSTRUCTION(xchg, 0x87)
REGULAR_INSTRUCTION(imul, 0xAF, 0x0F)
REGULAR_INSTRUCTION(bsr, 0xBD, 0x0F)
#undef REGULAR_INSTRUCTION
RA(Q, movsxd, 0x63)
RR(Q, movsxd, 0x63)
AR(L, movb, 0x88)
AR(L, movl, 0x89)
AR(Q, movq, 0x89)
AR(W, movw, 0x89)
RA(L, movb, 0x8A)
RA(L, movl, 0x8B)
RA(Q, movq, 0x8B)
RR(L, movl, 0x8B)
RA(Q, leaq, 0x8D)
AR(L, cmpxchgl, 0xB1, 0x0F)
AR(Q, cmpxchgq, 0xB1, 0x0F)
RA(L, cmpxchgl, 0xB1, 0x0F)
RA(Q, cmpxchgq, 0xB1, 0x0F)
RR(L, cmpxchgl, 0xB1, 0x0F)
RR(Q, cmpxchgq, 0xB1, 0x0F)
RA(Q, movzxb, 0xB6, 0x0F)
RR(Q, movzxb, 0xB6, 0x0F)
RA(Q, movzxw, 0xB7, 0x0F)
RR(Q, movzxw, 0xB7, 0x0F)
RA(Q, movsxb, 0xBE, 0x0F)
RR(Q, movsxb, 0xBE, 0x0F)
RA(Q, movsxw, 0xBF, 0x0F)
RR(Q, movsxw, 0xBF, 0x0F)
#define DECLARE_CMOV(name, code) \
RR(Q, cmov##name##q, 0x40 + code, 0x0F) \
RR(L, cmov##name##l, 0x40 + code, 0x0F) \
RA(Q, cmov##name##q, 0x40 + code, 0x0F) \
RA(L, cmov##name##l, 0x40 + code, 0x0F)
X86_CONDITIONAL_SUFFIXES(DECLARE_CMOV)
#undef DECLARE_CMOV
#undef AA
#undef RA
#undef AR
#define SIMPLE(name, ...) \
void name() { EmitSimple(__VA_ARGS__); }
SIMPLE(cpuid, 0x0F, 0xA2)
SIMPLE(fcos, 0xD9, 0xFF)
SIMPLE(fincstp, 0xD9, 0xF7)
SIMPLE(fsin, 0xD9, 0xFE)
SIMPLE(lock, 0xF0)
SIMPLE(rep_movsb, 0xF3, 0xA4)
#undef SIMPLE
// XmmRegister operations with another register or an address.
#define XX(width, name, ...) \
void name(XmmRegister dst, XmmRegister src) { \
Emit##width(dst, src, __VA_ARGS__); \
}
#define XA(width, name, ...) \
void name(XmmRegister dst, const Address& src) { \
Emit##width(dst, src, __VA_ARGS__); \
}
#define AX(width, name, ...) \
void name(const Address& dst, XmmRegister src) { \
Emit##width(src, dst, __VA_ARGS__); \
}
// We could add movupd here, but movups does the same and is shorter.
XA(L, movups, 0x10, 0x0F);
XA(L, movsd, 0x10, 0x0F, 0xF2)
XA(L, movss, 0x10, 0x0F, 0xF3)
AX(L, movups, 0x11, 0x0F);
AX(L, movsd, 0x11, 0x0F, 0xF2)
AX(L, movss, 0x11, 0x0F, 0xF3)
XX(L, movhlps, 0x12, 0x0F)
XX(L, unpcklps, 0x14, 0x0F)
XX(L, unpcklpd, 0x14, 0x0F, 0x66)
XX(L, unpckhps, 0x15, 0x0F)
XX(L, unpckhpd, 0x15, 0x0F, 0x66)
XX(L, movlhps, 0x16, 0x0F)
XX(L, movaps, 0x28, 0x0F)
XX(L, comisd, 0x2F, 0x0F, 0x66)
#define DECLARE_XMM(name, code) \
XX(L, name##ps, 0x50 + code, 0x0F) \
XA(L, name##ps, 0x50 + code, 0x0F) \
AX(L, name##ps, 0x50 + code, 0x0F) \
XX(L, name##pd, 0x50 + code, 0x0F, 0x66) \
XA(L, name##pd, 0x50 + code, 0x0F, 0x66) \
AX(L, name##pd, 0x50 + code, 0x0F, 0x66) \
XX(L, name##sd, 0x50 + code, 0x0F, 0xF2) \
XA(L, name##sd, 0x50 + code, 0x0F, 0xF2) \
AX(L, name##sd, 0x50 + code, 0x0F, 0xF2) \
XX(L, name##ss, 0x50 + code, 0x0F, 0xF3) \
XA(L, name##ss, 0x50 + code, 0x0F, 0xF3) \
AX(L, name##ss, 0x50 + code, 0x0F, 0xF3)
XMM_ALU_CODES(DECLARE_XMM)
#undef DECLARE_XMM
XX(L, cvtps2pd, 0x5A, 0x0F)
XX(L, cvtpd2ps, 0x5A, 0x0F, 0x66)
XX(L, cvtsd2ss, 0x5A, 0x0F, 0xF2)
XX(L, cvtss2sd, 0x5A, 0x0F, 0xF3)
XX(L, pxor, 0xEF, 0x0F, 0x66)
XX(L, subpl, 0xFA, 0x0F, 0x66)
XX(L, addpl, 0xFE, 0x0F, 0x66)
#undef XX
#undef AX
#undef XA
#define DECLARE_CMPPS(name, code) \
void cmpps##name(XmmRegister dst, XmmRegister src) { \
EmitL(dst, src, 0xC2, 0x0F); \
AssemblerBuffer::EnsureCapacity ensured(&buffer_); \
EmitUint8(code); \
}
XMM_CONDITIONAL_CODES(DECLARE_CMPPS)
#undef DECLARE_CMPPS
#define DECLARE_SIMPLE(name, opcode) \
void name() { EmitSimple(opcode); }
X86_ZERO_OPERAND_1_BYTE_INSTRUCTIONS(DECLARE_SIMPLE)
#undef DECLARE_SIMPLE
void movl(Register dst, const Immediate& imm);
void movl(Register dst, const Address& src);
void movl(const Address& dst, Register src);
void movl(const Address& dst, const Immediate& imm);
void movzxb(Register dst, Register src);
void movzxb(Register dst, const Address& src);
void movsxb(Register dst, Register src);
void movsxb(Register dst, const Address& src);
void movb(Register dst, const Address& src);
void movb(const Address& dst, Register src);
void movb(const Address& dst, const Immediate& imm);
void movzxw(Register dst, Register src);
void movzxw(Register dst, const Address& src);
void movsxw(Register dst, Register src);
void movsxw(Register dst, const Address& src);
void movw(Register dst, const Address& src);
void movw(const Address& dst, Register src);
void movw(const Address& dst, const Immediate& imm);
void movq(Register dst, const Immediate& imm);
void movq(Register dst, Register src);
void movq(Register dst, const Address& src);
void movq(const Address& dst, Register src);
void movq(const Address& dst, const Immediate& imm);
void movq(Register dst, XmmRegister src);
void movsxd(Register dst, Register src);
void movsxd(Register dst, const Address& src);
// Destination and source are reversed for some reason.
void movq(Register dst, XmmRegister src) {
EmitQ(src, dst, 0x7E, 0x0F, 0x66);
}
void movl(Register dst, XmmRegister src) {
EmitL(src, dst, 0x7E, 0x0F, 0x66);
}
void movss(XmmRegister dst, XmmRegister src) {
EmitL(src, dst, 0x11, 0x0F, 0xF3);
}
void movsd(XmmRegister dst, XmmRegister src) {
EmitL(src, dst, 0x11, 0x0F, 0xF2);
}
void rep_movsb();
// Use the reversed operand order and the 0x89 bytecode instead of the
// obvious 0x88 encoding for this some, because it is expected by gdb64 older
// than 7.3.1-gg5 when disassembling a function's prologue (movq rbp, rsp)
// for proper unwinding of Dart frames (use --generate_gdb_symbols and -O0).
void movq(Register dst, Register src) { EmitQ(src, dst, 0x89); }
void leaq(Register dst, const Address& src);
void movd(XmmRegister dst, Register src) {
EmitL(dst, src, 0x6E, 0x0F, 0x66);
}
void cvtsi2sdq(XmmRegister dst, Register src) {
EmitQ(dst, src, 0x2A, 0x0F, 0xF2);
}
void cvtsi2sdl(XmmRegister dst, Register src) {
EmitL(dst, src, 0x2A, 0x0F, 0xF2);
}
void cvttsd2siq(Register dst, XmmRegister src) {
EmitQ(dst, src, 0x2C, 0x0F, 0xF2);
}
void movmskpd(Register dst, XmmRegister src) {
EmitL(dst, src, 0x50, 0x0F, 0x66);
}
void movmskps(Register dst, XmmRegister src) { EmitL(dst, src, 0x50, 0x0F); }
void cmovnoq(Register dst, Register src);
void cmoveq(Register dst, Register src);
void cmovgeq(Register dst, Register src);
void cmovlessq(Register dst, Register src);
void btl(Register dst, Register src) { EmitL(src, dst, 0xA3, 0x0F); }
void btq(Register dst, Register src) { EmitQ(src, dst, 0xA3, 0x0F); }
void movss(XmmRegister dst, const Address& src);
void movss(const Address& dst, XmmRegister src);
void movss(XmmRegister dst, XmmRegister src);
void movd(XmmRegister dst, Register src);
void movd(Register dst, XmmRegister src);
void addss(XmmRegister dst, XmmRegister src);
void subss(XmmRegister dst, XmmRegister src);
void mulss(XmmRegister dst, XmmRegister src);
void divss(XmmRegister dst, XmmRegister src);
void movsd(XmmRegister dst, const Address& src);
void movsd(const Address& dst, XmmRegister src);
void movsd(XmmRegister dst, XmmRegister src);
void movaps(XmmRegister dst, XmmRegister src);
void movups(const Address& dst, XmmRegister src);
void movups(XmmRegister dst, const Address& src);
void addsd(XmmRegister dst, XmmRegister src);
void subsd(XmmRegister dst, XmmRegister src);
void mulsd(XmmRegister dst, XmmRegister src);
void divsd(XmmRegister dst, XmmRegister src);
void addpl(XmmRegister dst, XmmRegister src);
void subpl(XmmRegister dst, XmmRegister src);
void addps(XmmRegister dst, XmmRegister src);
void subps(XmmRegister dst, XmmRegister src);
void divps(XmmRegister dst, XmmRegister src);
void mulps(XmmRegister dst, XmmRegister src);
void minps(XmmRegister dst, XmmRegister src);
void maxps(XmmRegister dst, XmmRegister src);
void andps(XmmRegister dst, XmmRegister src);
void andps(XmmRegister dst, const Address& src);
void orps(XmmRegister dst, XmmRegister src);
void notps(XmmRegister dst);
void negateps(XmmRegister dst);
void absps(XmmRegister dst);
void zerowps(XmmRegister dst);
void cmppseq(XmmRegister dst, XmmRegister src);
void cmppsneq(XmmRegister dst, XmmRegister src);
void cmppslt(XmmRegister dst, XmmRegister src);
void cmppsle(XmmRegister dst, XmmRegister src);
void cmppsnlt(XmmRegister dst, XmmRegister src);
void cmppsnle(XmmRegister dst, XmmRegister src);
void sqrtps(XmmRegister dst);
void rsqrtps(XmmRegister dst);
void reciprocalps(XmmRegister dst);
void movhlps(XmmRegister dst, XmmRegister src);
void movlhps(XmmRegister dst, XmmRegister src);
void unpcklps(XmmRegister dst, XmmRegister src);
void unpckhps(XmmRegister dst, XmmRegister src);
void unpcklpd(XmmRegister dst, XmmRegister src);
void unpckhpd(XmmRegister dst, XmmRegister src);
void notps(XmmRegister dst, XmmRegister src);
void negateps(XmmRegister dst, XmmRegister src);
void absps(XmmRegister dst, XmmRegister src);
void zerowps(XmmRegister dst, XmmRegister src);
void set1ps(XmmRegister dst, Register tmp, const Immediate& imm);
void shufps(XmmRegister dst, XmmRegister src, const Immediate& mask);
void addpd(XmmRegister dst, XmmRegister src);
void negatepd(XmmRegister dst);
void subpd(XmmRegister dst, XmmRegister src);
void mulpd(XmmRegister dst, XmmRegister src);
void divpd(XmmRegister dst, XmmRegister src);
void abspd(XmmRegister dst);
void minpd(XmmRegister dst, XmmRegister src);
void maxpd(XmmRegister dst, XmmRegister src);
void sqrtpd(XmmRegister dst);
void cvtps2pd(XmmRegister dst, XmmRegister src);
void cvtpd2ps(XmmRegister dst, XmmRegister src);
void negatepd(XmmRegister dst, XmmRegister src);
void abspd(XmmRegister dst, XmmRegister src);
void shufpd(XmmRegister dst, XmmRegister src, const Immediate& mask);
void comisd(XmmRegister a, XmmRegister b);
void cvtsi2sdq(XmmRegister a, Register b);
void cvtsi2sdl(XmmRegister a, Register b);
void cvttsd2siq(Register dst, XmmRegister src);
void cvtss2sd(XmmRegister dst, XmmRegister src);
void cvtsd2ss(XmmRegister dst, XmmRegister src);
void pxor(XmmRegister dst, XmmRegister src);
enum RoundingMode {
kRoundToNearest = 0x0,
kRoundDown = 0x1,
@ -502,20 +579,12 @@ class Assembler : public ValueObject {
};
void roundsd(XmmRegister dst, XmmRegister src, RoundingMode mode);
void xchgl(Register dst, Register src);
void xchgq(Register dst, Register src);
void cmpb(const Address& address, const Immediate& imm);
void cmpw(const Address& address, const Immediate& imm);
void CompareImmediate(Register reg, const Immediate& imm);
void CompareImmediate(const Address& address, const Immediate& imm);
void testl(Register reg1, Register reg2);
void testl(Register reg, const Immediate& imm) { testq(reg, imm); }
void testb(const Address& address, const Immediate& imm);
void testq(Register reg1, Register reg2);
void testq(Register reg, const Immediate& imm);
void TestImmediate(Register dst, const Immediate& imm);
@ -523,66 +592,61 @@ class Assembler : public ValueObject {
void OrImmediate(Register dst, const Immediate& imm);
void XorImmediate(Register dst, const Immediate& imm);
// clang-format off
// Macro for handling common ALU instructions. Arguments to F:
// name, opcode, reversed opcode, opcode for the reg field of the modrm byte.
#define ALU_OPS(F) \
F(and, 0x23, 0x21, 4) \
F(or, 0x0b, 0x09, 1) \
F(xor, 0x33, 0x31, 6) \
F(add, 0x03, 0x01, 0) \
F(adc, 0x13, 0x11, 2) \
F(sub, 0x2b, 0x29, 5) \
F(sbb, 0x1b, 0x19, 3) \
F(cmp, 0x3b, 0x39, 7)
// clang-format on
#define DECLARE_ALU(op, opcode, opcode2, modrm_opcode) \
void op##w(Register dst, Register src) { Alu(2, opcode, dst, src); } \
void op##l(Register dst, Register src) { Alu(4, opcode, dst, src); } \
void op##q(Register dst, Register src) { Alu(8, opcode, dst, src); } \
void op##w(Register dst, const Address& src) { Alu(2, opcode, dst, src); } \
void op##l(Register dst, const Address& src) { Alu(4, opcode, dst, src); } \
void op##q(Register dst, const Address& src) { Alu(8, opcode, dst, src); } \
void op##w(const Address& dst, Register src) { Alu(2, opcode2, dst, src); } \
void op##l(const Address& dst, Register src) { Alu(4, opcode2, dst, src); } \
void op##q(const Address& dst, Register src) { Alu(8, opcode2, dst, src); } \
void op##l(Register dst, const Immediate& imm) { \
AluL(modrm_opcode, dst, imm); \
} \
void op##q(Register dst, const Immediate& imm) { \
AluQ(modrm_opcode, opcode, dst, imm); \
} \
void op##l(const Address& dst, const Immediate& imm) { \
AluL(modrm_opcode, dst, imm); \
} \
void op##q(const Address& dst, const Immediate& imm) { \
AluQ(modrm_opcode, opcode, dst, imm); \
void shldq(Register dst, Register src, Register shifter) {
ASSERT(shifter == RCX);
EmitQ(src, dst, 0xA5, 0x0F);
}
void shrdq(Register dst, Register src, Register shifter) {
ASSERT(shifter == RCX);
EmitQ(src, dst, 0xAD, 0x0F);
}
ALU_OPS(DECLARE_ALU);
#define DECLARE_ALU(op, c) \
void op##w(Register dst, Register src) { EmitW(dst, src, c * 8 + 3); } \
void op##l(Register dst, Register src) { EmitL(dst, src, c * 8 + 3); } \
void op##q(Register dst, Register src) { EmitQ(dst, src, c * 8 + 3); } \
void op##w(Register dst, const Address& src) { EmitW(dst, src, c * 8 + 3); } \
void op##l(Register dst, const Address& src) { EmitL(dst, src, c * 8 + 3); } \
void op##q(Register dst, const Address& src) { EmitQ(dst, src, c * 8 + 3); } \
void op##w(const Address& dst, Register src) { EmitW(src, dst, c * 8 + 1); } \
void op##l(const Address& dst, Register src) { EmitL(src, dst, c * 8 + 1); } \
void op##q(const Address& dst, Register src) { EmitQ(src, dst, c * 8 + 1); } \
void op##l(Register dst, const Immediate& imm) { AluL(c, dst, imm); } \
void op##q(Register dst, const Immediate& imm) { \
AluQ(c, c * 8 + 3, dst, imm); \
} \
void op##b(const Address& dst, const Immediate& imm) { AluB(c, dst, imm); } \
void op##w(const Address& dst, const Immediate& imm) { AluW(c, dst, imm); } \
void op##l(const Address& dst, const Immediate& imm) { AluL(c, dst, imm); } \
void op##q(const Address& dst, const Immediate& imm) { \
AluQ(c, c * 8 + 3, dst, imm); \
}
X86_ALU_CODES(DECLARE_ALU)
#undef DECLARE_ALU
#undef ALU_OPS
void cdq();
void cqo();
void idivl(Register reg);
void divl(Register reg);
#define REGULAR_UNARY(name, opcode, modrm) \
void name##q(Register reg) { EmitUnaryQ(reg, opcode, modrm); } \
void name##l(Register reg) { EmitUnaryL(reg, opcode, modrm); } \
void name##q(const Address& address) { EmitUnaryQ(address, opcode, modrm); } \
void name##l(const Address& address) { EmitUnaryL(address, opcode, modrm); }
REGULAR_UNARY(not, 0xF7, 2)
REGULAR_UNARY(neg, 0xF7, 3)
REGULAR_UNARY(mul, 0xF7, 4)
REGULAR_UNARY(div, 0xF7, 6)
REGULAR_UNARY(idiv, 0xF7, 7)
REGULAR_UNARY(inc, 0xFF, 0)
REGULAR_UNARY(dec, 0xFF, 1)
#undef REGULAR_UNARY
void idivq(Register reg);
void divq(Register reg);
void imull(Register dst, Register src);
void imull(Register reg, const Immediate& imm);
void mull(Register reg);
void imulq(Register dst, Register src);
void imulq(Register dst, const Address& address);
void imulq(Register dst, const Immediate& imm);
void MulImmediate(Register reg, const Immediate& imm);
void mulq(Register reg);
void shll(Register reg, const Immediate& imm);
void shll(Register operand, Register shifter);
@ -599,75 +663,28 @@ class Assembler : public ValueObject {
void sarq(Register reg, const Immediate& imm);
void sarq(Register operand, Register shifter);
void shldq(Register dst, Register src, const Immediate& imm);
void shldq(Register dst, Register src, Register shifter);
void shrdq(Register dst, Register src, Register shifter);
void incl(const Address& address);
void decl(const Address& address);
void incq(Register reg);
void incq(const Address& address);
void decq(Register reg);
void decq(const Address& address);
void negl(Register reg);
void negq(Register reg);
void notl(Register reg);
void notq(Register reg);
void bsrq(Register dst, Register src);
void btq(Register base, Register offset);
void btq(Register base, int bit);
void enter(const Immediate& imm);
void leave();
void ret();
void movmskpd(Register dst, XmmRegister src);
void movmskps(Register dst, XmmRegister src);
void sqrtsd(XmmRegister dst, XmmRegister src);
void xorpd(XmmRegister dst, const Address& src);
void xorpd(XmmRegister dst, XmmRegister src);
void xorps(XmmRegister dst, const Address& src);
void xorps(XmmRegister dst, XmmRegister src);
void andpd(XmmRegister dst, const Address& src);
void fldl(const Address& src);
void fstpl(const Address& dst);
void fincstp();
void ffree(intptr_t value);
void fsin();
void fcos();
// 'size' indicates size in bytes and must be in the range 1..8.
void nop(int size = 1);
void int3();
void hlt();
static uword GetBreakInstructionFiller() { return 0xCCCCCCCCCCCCCCCC; }
void j(Condition condition, Label* label, bool near = kFarJump);
void jmp(Register reg);
void jmp(const Address& address);
void jmp(Register reg) { EmitUnaryL(reg, 0xFF, 4); }
void jmp(const Address& address) { EmitUnaryL(address, 0xFF, 4); }
void jmp(Label* label, bool near = kFarJump);
void jmp(const ExternalLabel* label);
void jmp(const StubEntry& stub_entry);
void lock();
void cmpxchgl(const Address& address, Register reg);
void cmpxchgq(const Address& address, Register reg);
void cpuid();
// Issue memory to memory move through a TMP register.
// TODO(koda): Assert that these are not used for heap objects.
void MoveMemoryToMemory(const Address& dst, const Address& src) {
@ -688,10 +705,7 @@ class Assembler : public ValueObject {
xorq(mem2, TMP);
}
/*
* Macros for High-level operations and implemented on all architectures.
*/
// Methods for High-level operations and implemented on all architectures.
void CompareRegisters(Register a, Register b);
void BranchIf(Condition condition, Label* label) { j(condition, label); }
@ -700,7 +714,7 @@ class Assembler : public ValueObject {
void PushRegister(Register r);
void PopRegister(Register r);
// Macros for adding/subtracting an immediate value that may be loaded from
// Methods for adding/subtracting an immediate value that may be loaded from
// the constant pool.
// TODO(koda): Assert that these are not used for heap objects.
void AddImmediate(Register reg, const Immediate& imm);
@ -760,10 +774,8 @@ class Assembler : public ValueObject {
// Increments a Smi field. Leaves flags in same state as an 'addq'.
void IncrementSmiField(const Address& dest, int64_t increment);
void DoubleNegate(XmmRegister d);
void FloatNegate(XmmRegister f);
void DoubleAbs(XmmRegister reg);
void DoubleNegate(XmmRegister dst, XmmRegister src);
void DoubleAbs(XmmRegister dst, XmmRegister src);
void LockCmpxchgq(const Address& address, Register reg) {
lock();
@ -796,9 +808,7 @@ class Assembler : public ValueObject {
// if platform ABI requires that. Does not restore RSP after the call itself.
void CallCFunction(Register reg);
/*
* Loading and comparing classes of objects.
*/
// Loading and comparing classes of objects.
void LoadClassId(Register result, Register object);
void LoadClassById(Register result, Register class_id);
@ -816,9 +826,7 @@ class Assembler : public ValueObject {
// Value in the register object is untagged optimistically.
void SmiUntagOrCheckClass(Register object, intptr_t class_id, Label* smi);
/*
* Misc. functionality.
*/
// Misc. functionality.
void SmiTag(Register reg) { addq(reg, reg); }
void SmiUntag(Register reg) { sarq(reg, Immediate(kSmiTagSize)); }
@ -1010,10 +1018,9 @@ class Assembler : public ValueObject {
void LoadObjectHelper(Register dst, const Object& obj, bool is_unique);
void LoadWordFromPoolOffset(Register dst, int32_t offset);
void Alu(int bytes, uint8_t opcode, Register dst, Register src);
void Alu(int bytes, uint8_t opcode, Register dst, const Address& src);
void Alu(int bytes, uint8_t opcode, const Address& dst, Register src);
void AluL(uint8_t modrm_opcode, Register dst, const Immediate& imm);
void AluB(uint8_t modrm_opcode, const Address& dst, const Immediate& imm);
void AluW(uint8_t modrm_opcode, const Address& dst, const Immediate& imm);
void AluL(uint8_t modrm_opcode, const Address& dst, const Immediate& imm);
void AluQ(uint8_t modrm_opcode,
uint8_t opcode,
@ -1024,6 +1031,37 @@ class Assembler : public ValueObject {
const Address& dst,
const Immediate& imm);
void EmitSimple(int opcode, int opcode2 = -1);
void EmitUnaryQ(Register reg, int opcode, int modrm_code);
void EmitUnaryL(Register reg, int opcode, int modrm_code);
void EmitUnaryQ(const Address& address, int opcode, int modrm_code);
void EmitUnaryL(const Address& address, int opcode, int modrm_code);
// The prefixes are in reverse order due to the rules of default arguments in
// C++.
void EmitQ(int reg,
const Address& address,
int opcode,
int prefix2 = -1,
int prefix1 = -1);
void EmitL(int reg,
const Address& address,
int opcode,
int prefix2 = -1,
int prefix1 = -1);
void EmitW(Register reg,
const Address& address,
int opcode,
int prefix2 = -1,
int prefix1 = -1);
void EmitQ(int dst, int src, int opcode, int prefix2 = -1, int prefix1 = -1);
void EmitL(int dst, int src, int opcode, int prefix2 = -1, int prefix1 = -1);
void EmitW(Register dst,
Register src,
int opcode,
int prefix2 = -1,
int prefix1 = -1);
void CmpPS(XmmRegister dst, XmmRegister src, int condition);
inline void EmitUint8(uint8_t value);
inline void EmitInt32(int32_t value);
inline void EmitUInt32(uint32_t value);
@ -1033,24 +1071,16 @@ class Assembler : public ValueObject {
uint8_t rex,
bool force_emit = false);
inline void EmitOperandREX(int rm, const Operand& operand, uint8_t rex);
inline void EmitXmmRegisterOperand(int rm, XmmRegister reg);
inline void EmitRegisterOperand(int rm, int reg);
inline void EmitFixup(AssemblerFixup* fixup);
inline void EmitOperandSizeOverride();
inline void EmitREX_RB(XmmRegister reg,
XmmRegister base,
uint8_t rex = REX_NONE);
inline void EmitREX_RB(XmmRegister reg,
const Operand& operand,
uint8_t rex = REX_NONE);
inline void EmitREX_RB(XmmRegister reg,
Register base,
uint8_t rex = REX_NONE);
inline void EmitREX_RB(Register reg,
XmmRegister base,
uint8_t rex = REX_NONE);
inline void EmitRegRegRex(int reg, int base, uint8_t rex = REX_NONE);
void EmitOperand(int rm, const Operand& operand);
void EmitImmediate(const Immediate& imm);
void EmitComplex(int rm, const Operand& operand, const Immediate& immediate);
void EmitSignExtendedInt8(int rm,
const Operand& operand,
const Immediate& immediate);
void EmitLabel(Label* label, intptr_t instruction_size);
void EmitLabelLink(Label* label);
void EmitNearLabelLink(Label* label);
@ -1092,7 +1122,8 @@ inline void Assembler::EmitInt64(int64_t value) {
}
inline void Assembler::EmitRegisterREX(Register reg, uint8_t rex, bool force) {
ASSERT(reg != kNoRegister);
ASSERT(reg != kNoRegister && reg <= R15);
ASSERT(rex == REX_NONE || rex == REX_W);
rex |= (reg > 7 ? REX_B : REX_NONE);
if (rex != REX_NONE || force) EmitUint8(REX_PREFIX | rex);
}
@ -1104,29 +1135,10 @@ inline void Assembler::EmitOperandREX(int rm,
if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex);
}
inline void Assembler::EmitREX_RB(XmmRegister reg,
XmmRegister base,
uint8_t rex) {
if (reg > 7) rex |= REX_R;
if (base > 7) rex |= REX_B;
if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex);
}
inline void Assembler::EmitREX_RB(XmmRegister reg,
const Operand& operand,
uint8_t rex) {
if (reg > 7) rex |= REX_R;
rex |= operand.rex();
if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex);
}
inline void Assembler::EmitREX_RB(XmmRegister reg, Register base, uint8_t rex) {
if (reg > 7) rex |= REX_R;
if (base > 7) rex |= REX_B;
if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex);
}
inline void Assembler::EmitREX_RB(Register reg, XmmRegister base, uint8_t rex) {
inline void Assembler::EmitRegRegRex(int reg, int base, uint8_t rex) {
ASSERT(reg != kNoRegister && reg <= R15);
ASSERT(base != kNoRegister && base <= R15);
ASSERT(rex == REX_NONE || rex == REX_W);
if (reg > 7) rex |= REX_R;
if (base > 7) rex |= REX_B;
if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex);

View file

@ -959,7 +959,7 @@ ASSEMBLER_TEST_RUN(SignedDivide, test) {
EXPECT_DISASSEMBLY(
"movl rax,-0x........\n"
"movl rdx,0x7b\n"
"cdql\n"
"cdq\n"
"movl rcx,0x2a\n"
"idivl (rax,rdx),rcx\n"
"ret\n");
@ -1004,7 +1004,7 @@ ASSEMBLER_TEST_RUN(SignedDivideLong, test) {
EXPECT_DISASSEMBLY(
"movq rax,0x................\n"
"movl rdx,0x7b\n"
"cdqq\n"
"cqo\n"
"movl rcx,0x2a\n"
"idivq (rax,rdx),rcx\n"
"ret\n");
@ -1204,6 +1204,60 @@ ASSEMBLER_TEST_RUN(MoveWord, test) {
"ret\n");
}
ASSEMBLER_TEST_GENERATE(WordOps, assembler) {
__ movq(RAX, Immediate(0x0102030405060708));
__ pushq(RAX);
__ addw(Address(RSP, 0), Immediate(-0x201));
__ subw(Address(RSP, 2), Immediate(0x201));
__ xorw(Address(RSP, 4), Immediate(0x201));
__ andw(Address(RSP, 6), Immediate(0x301));
__ andw(Address(RSP, 0), Immediate(-1));
__ popq(RAX);
__ ret();
}
ASSEMBLER_TEST_RUN(WordOps, test) {
typedef int64_t (*WordOps)();
EXPECT_EQ(0x0100010503050507, reinterpret_cast<WordOps>(test->entry())());
EXPECT_DISASSEMBLY(
"movq rax,0x................\n"
"push rax\n"
"addw [rsp],0x....\n"
"subw [rsp+0x2],0x...\n"
"xorw [rsp+0x4],0x...\n"
"andw [rsp+0x6],0x...\n"
"andw [rsp],-1\n"
"pop rax\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(ByteOps, assembler) {
__ movq(RAX, Immediate(0x0102030405060708));
__ pushq(RAX);
__ addb(Address(RSP, 0), Immediate(0xff));
__ subb(Address(RSP, 2), Immediate(1));
__ xorb(Address(RSP, 4), Immediate(1));
__ andb(Address(RSP, 6), Immediate(1));
__ andb(Address(RSP, 0), Immediate(-1));
__ popq(RAX);
__ ret();
}
ASSEMBLER_TEST_RUN(ByteOps, test) {
typedef int64_t (*ByteOps)();
EXPECT_EQ(0x0100030505050707, reinterpret_cast<ByteOps>(test->entry())());
EXPECT_DISASSEMBLY(
"movq rax,0x................\n"
"push rax\n"
"addb [rsp],-1\n"
"subb [rsp+0x2],1\n"
"xorb [rsp+0x4],1\n"
"andb [rsp+0x6],1\n"
"andb [rsp],-1\n"
"pop rax\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(MoveWordRex, assembler) {
__ pushq(Immediate(0));
__ movq(R8, RSP);
@ -3161,7 +3215,7 @@ ASSEMBLER_TEST_GENERATE(PackedDoubleNegate, assembler) {
EnterTestFrame(assembler);
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
__ movups(XMM10, Address(RAX, 0));
__ negatepd(XMM10);
__ negatepd(XMM10, XMM10);
__ movaps(XMM0, XMM10);
LeaveTestFrame(assembler);
__ ret();
@ -3191,8 +3245,7 @@ ASSEMBLER_TEST_GENERATE(PackedDoubleAbsolute, assembler) {
EnterTestFrame(assembler);
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
__ movups(XMM10, Address(RAX, 0));
__ abspd(XMM10);
__ movaps(XMM0, XMM10);
__ abspd(XMM0, XMM10);
LeaveTestFrame(assembler);
__ ret();
}
@ -3203,8 +3256,8 @@ ASSEMBLER_TEST_RUN(PackedDoubleAbsolute, test) {
EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(
"movups xmm10,[rax]\n"
"movq r11,[thr+0x...]\n"
"andpd xmm10,[r11]\n"
"movaps xmm0,xmm10\n"
"movups xmm0,[r11]\n"
"andpd xmm0,xmm10\n"
"pop thr\n"
"pop pp\n"
"pop r12\n"
@ -3278,7 +3331,7 @@ ASSEMBLER_TEST_GENERATE(PackedDoubleSqrt, assembler) {
} constant0 = {16.0, 2.0};
__ movq(RAX, Immediate(reinterpret_cast<uword>(&constant0)));
__ movups(XMM10, Address(RAX, 0));
__ sqrtpd(XMM10);
__ sqrtpd(XMM10, XMM10);
__ movaps(XMM0, XMM10);
__ ret();
}
@ -3423,7 +3476,7 @@ ASSEMBLER_TEST_RUN(PackedSingleToDouble, test) {
EXPECT_FLOAT_EQ(9.0f, res, 0.000001f);
EXPECT_DISASSEMBLY_ENDS_WITH(
"movups xmm11,[rax]\n"
"cvtsd2ss xmm10,xmm11\n"
"cvtps2pd xmm10,xmm11\n"
"movaps xmm0,xmm10\n"
"ret\n");
}
@ -3604,9 +3657,9 @@ ASSEMBLER_TEST_GENERATE(PackedFPOperations2, assembler) {
__ shufps(XMM0, XMM0, Immediate(0x0));
__ movaps(XMM11, XMM0); // Copy XMM0
__ reciprocalps(XMM11); // 0.25
__ sqrtps(XMM11); // 0.5
__ rsqrtps(XMM0); // ~0.5
__ rcpps(XMM11, XMM11); // 0.25
__ sqrtps(XMM11, XMM11); // 0.5
__ rsqrtps(XMM0, XMM0); // ~0.5
__ subps(XMM0, XMM11); // ~0.0
__ shufps(XMM0, XMM0, Immediate(0x00)); // Copy second lane into all 4 lanes.
__ ret();
@ -3650,13 +3703,44 @@ ASSEMBLER_TEST_RUN(PackedCompareEQ, test) {
"movl rax,0x........\n"
"movd xmm1,rax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [0]\n"
"cmpps xmm0,xmm1 [eq]\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(XmmAlu, assembler) {
// Test the disassembler.
__ addss(XMM0, XMM0);
__ addsd(XMM0, XMM0);
__ addps(XMM0, XMM0);
__ addpd(XMM0, XMM0);
__ cvtss2sd(XMM0, XMM0);
__ cvtsd2ss(XMM0, XMM0);
__ cvtps2pd(XMM0, XMM0);
__ cvtpd2ps(XMM0, XMM0);
__ movl(RAX, Immediate(0));
__ ret();
}
ASSEMBLER_TEST_RUN(XmmAlu, test) {
typedef intptr_t (*XmmAluTest)();
intptr_t res = reinterpret_cast<XmmAluTest>(test->entry())();
EXPECT_EQ(res, 0);
EXPECT_DISASSEMBLY(
"addss xmm0,xmm0\n"
"addsd xmm0,xmm0\n"
"addps xmm0,xmm0\n"
"addpd xmm0,xmm0\n"
"cvtss2sd xmm0,xmm0\n"
"cvtsd2ss xmm0,xmm0\n"
"cvtps2pd xmm0,xmm0\n"
"cvtpd2ps xmm0,xmm0\n"
"movl rax,0\n"
"ret\n");
}
ASSEMBLER_TEST_GENERATE(PackedCompareNEQ, assembler) {
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(2.0f)));
__ set1ps(XMM1, RAX, Immediate(bit_cast<int32_t, float>(4.0f)));
@ -3678,7 +3762,7 @@ ASSEMBLER_TEST_RUN(PackedCompareNEQ, test) {
"movl rax,0x........\n"
"movd xmm1,rax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [4]\n"
"cmpps xmm0,xmm1 [neq]\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
@ -3706,7 +3790,7 @@ ASSEMBLER_TEST_RUN(PackedCompareLT, test) {
"movl rax,0x........\n"
"movd xmm1,rax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [1]\n"
"cmpps xmm0,xmm1 [lt]\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
@ -3734,7 +3818,7 @@ ASSEMBLER_TEST_RUN(PackedCompareLE, test) {
"movl rax,0x........\n"
"movd xmm1,rax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [2]\n"
"cmpps xmm0,xmm1 [le]\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
@ -3762,7 +3846,7 @@ ASSEMBLER_TEST_RUN(PackedCompareNLT, test) {
"movl rax,0x........\n"
"movd xmm1,rax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [5]\n"
"cmpps xmm0,xmm1 [nlt]\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
@ -3790,7 +3874,7 @@ ASSEMBLER_TEST_RUN(PackedCompareNLE, test) {
"movl rax,0x........\n"
"movd xmm1,rax\n"
"shufps xmm1,xmm1 [0]\n"
"cmpps xmm0,xmm1 [6]\n"
"cmpps xmm0,xmm1 [nle]\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
@ -3802,7 +3886,7 @@ ASSEMBLER_TEST_GENERATE(PackedNegate, assembler) {
__ movl(RAX, Immediate(bit_cast<int32_t, float>(12.3f)));
__ movd(XMM0, RAX);
__ shufps(XMM0, XMM0, Immediate(0x0));
__ negateps(XMM0);
__ negateps(XMM0, XMM0);
__ shufps(XMM0, XMM0, Immediate(0xAA)); // Copy third lane into all 4 lanes.
LeaveTestFrame(assembler);
__ ret();
@ -3839,7 +3923,7 @@ ASSEMBLER_TEST_GENERATE(PackedAbsolute, assembler) {
__ movl(RAX, Immediate(bit_cast<int32_t, float>(-15.3f)));
__ movd(XMM0, RAX);
__ shufps(XMM0, XMM0, Immediate(0x0));
__ absps(XMM0);
__ absps(XMM0, XMM0);
__ shufps(XMM0, XMM0, Immediate(0xAA)); // Copy third lane into all 4 lanes.
LeaveTestFrame(assembler);
__ ret();
@ -3874,7 +3958,7 @@ ASSEMBLER_TEST_RUN(PackedAbsolute, test) {
ASSEMBLER_TEST_GENERATE(PackedSetWZero, assembler) {
EnterTestFrame(assembler);
__ set1ps(XMM0, RAX, Immediate(bit_cast<int32_t, float>(12.3f)));
__ zerowps(XMM0);
__ zerowps(XMM0, XMM0);
__ shufps(XMM0, XMM0, Immediate(0xFF)); // Copy the W lane which is now 0.0.
LeaveTestFrame(assembler);
__ ret();
@ -4032,8 +4116,7 @@ ASSEMBLER_TEST_GENERATE(PackedLogicalNot, assembler) {
EnterTestFrame(assembler);
__ LoadImmediate(RAX, Immediate(reinterpret_cast<intptr_t>(&constant1)));
__ movups(XMM9, Address(RAX, 0));
__ notps(XMM9);
__ movaps(XMM0, XMM9);
__ notps(XMM0, XMM9);
__ pushq(RAX);
__ movss(Address(RSP, 0), XMM0);
__ popq(RAX);
@ -4047,8 +4130,8 @@ ASSEMBLER_TEST_RUN(PackedLogicalNot, test) {
EXPECT_DISASSEMBLY_NOT_WINDOWS_ENDS_WITH(
"movups xmm9,[rax]\n"
"movq r11,[thr+0x...]\n"
"xorps xmm9,[r11]\n"
"movaps xmm0,xmm9\n"
"movups xmm0,[r11]\n"
"xorps xmm0,xmm9\n"
"push rax\n"
"movss [rsp],xmm0\n"
"pop rax\n"
@ -5036,12 +5119,11 @@ ASSEMBLER_TEST_GENERATE(DoubleAbs, assembler) {
#if defined(HOST_OS_WINDOWS)
// First argument is code object, second argument is thread. MSVC passes
// third argument in XMM2.
__ DoubleAbs(XMM2);
__ movaps(XMM0, XMM2);
__ DoubleAbs(XMM0, XMM2);
#else
// SysV ABI allocates integral and double registers for arguments
// independently.
__ DoubleAbs(XMM0);
__ DoubleAbs(XMM0, XMM0);
#endif
LeaveTestFrame(assembler);
__ ret();
@ -5221,7 +5303,7 @@ ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) {
"movq rsi,[rsp+0x10]\n"
"movq rdi,[rsp+0x8]\n"
"movq rcx,[rsp]\n"
"rep movsl\n"
"rep movsb\n"
"pop rax\n"
"pop rax\n"
"pop rax\n"
@ -5234,7 +5316,7 @@ ASSEMBLER_TEST_GENERATE(ConditionalMovesCompare, assembler) {
__ cmpq(CallingConventions::kArg1Reg, CallingConventions::kArg2Reg);
__ movq(RDX, Immediate(1)); // Greater equal.
__ movq(RCX, Immediate(-1)); // Less
__ cmovlessq(RAX, RCX);
__ cmovlq(RAX, RCX);
__ cmovgeq(RAX, RDX);
__ ret();
}

View file

@ -41,40 +41,13 @@ struct ByteMnemonic {
const char* mnem;
};
#define ALU_ENTRY(name, code) \
{code * 8 + 0, BYTE_OPER_REG_OP_ORDER, #name}, \
{code * 8 + 1, OPER_REG_OP_ORDER, #name}, \
{code * 8 + 2, BYTE_REG_OPER_OP_ORDER, #name}, \
{code * 8 + 3, REG_OPER_OP_ORDER, #name},
static const ByteMnemonic two_operands_instr[] = {
{0x00, BYTE_OPER_REG_OP_ORDER, "add"},
{0x01, OPER_REG_OP_ORDER, "add"},
{0x02, BYTE_REG_OPER_OP_ORDER, "add"},
{0x03, REG_OPER_OP_ORDER, "add"},
{0x08, BYTE_OPER_REG_OP_ORDER, "or"},
{0x09, OPER_REG_OP_ORDER, "or"},
{0x0A, BYTE_REG_OPER_OP_ORDER, "or"},
{0x0B, REG_OPER_OP_ORDER, "or"},
{0x10, BYTE_OPER_REG_OP_ORDER, "adc"},
{0x11, OPER_REG_OP_ORDER, "adc"},
{0x12, BYTE_REG_OPER_OP_ORDER, "adc"},
{0x13, REG_OPER_OP_ORDER, "adc"},
{0x18, BYTE_OPER_REG_OP_ORDER, "sbb"},
{0x19, OPER_REG_OP_ORDER, "sbb"},
{0x1A, BYTE_REG_OPER_OP_ORDER, "sbb"},
{0x1B, REG_OPER_OP_ORDER, "sbb"},
{0x20, BYTE_OPER_REG_OP_ORDER, "and"},
{0x21, OPER_REG_OP_ORDER, "and"},
{0x22, BYTE_REG_OPER_OP_ORDER, "and"},
{0x23, REG_OPER_OP_ORDER, "and"},
{0x28, BYTE_OPER_REG_OP_ORDER, "sub"},
{0x29, OPER_REG_OP_ORDER, "sub"},
{0x2A, BYTE_REG_OPER_OP_ORDER, "sub"},
{0x2B, REG_OPER_OP_ORDER, "sub"},
{0x30, BYTE_OPER_REG_OP_ORDER, "xor"},
{0x31, OPER_REG_OP_ORDER, "xor"},
{0x32, BYTE_REG_OPER_OP_ORDER, "xor"},
{0x33, REG_OPER_OP_ORDER, "xor"},
{0x38, BYTE_OPER_REG_OP_ORDER, "cmp"},
{0x39, OPER_REG_OP_ORDER, "cmp"},
{0x3A, BYTE_REG_OPER_OP_ORDER, "cmp"},
{0x3B, REG_OPER_OP_ORDER, "cmp"},
{0x63, REG_OPER_OP_ORDER, "movsxd"},
X86_ALU_CODES(ALU_ENTRY){0x63, REG_OPER_OP_ORDER, "movsxd"},
{0x84, BYTE_REG_OPER_OP_ORDER, "test"},
{0x85, REG_OPER_OP_ORDER, "test"},
{0x86, BYTE_REG_OPER_OP_ORDER, "xchg"},
@ -86,31 +59,29 @@ static const ByteMnemonic two_operands_instr[] = {
{0x8D, REG_OPER_OP_ORDER, "lea"},
{-1, UNSET_OP_ORDER, ""}};
#define ZERO_OPERAND_ENTRY(name, opcode) {opcode, UNSET_OP_ORDER, #name},
static const ByteMnemonic zero_operands_instr[] = {
{0xC3, UNSET_OP_ORDER, "ret"}, {0xC9, UNSET_OP_ORDER, "leave"},
{0xF4, UNSET_OP_ORDER, "hlt"}, {0xFC, UNSET_OP_ORDER, "cld"},
{0xCC, UNSET_OP_ORDER, "int3"}, {0x60, UNSET_OP_ORDER, "pushad"},
{0x61, UNSET_OP_ORDER, "popad"}, {0x9C, UNSET_OP_ORDER, "pushfd"},
{0x9D, UNSET_OP_ORDER, "popfd"}, {0x9E, UNSET_OP_ORDER, "sahf"},
{0x99, UNSET_OP_ORDER, "cdq"}, {0x9B, UNSET_OP_ORDER, "fwait"},
{0xA4, UNSET_OP_ORDER, "movs"}, {0xA5, UNSET_OP_ORDER, "movs"},
{0xA6, UNSET_OP_ORDER, "cmps"}, {0xA7, UNSET_OP_ORDER, "cmps"},
{-1, UNSET_OP_ORDER, ""}};
X86_ZERO_OPERAND_1_BYTE_INSTRUCTIONS(ZERO_OPERAND_ENTRY){-1, UNSET_OP_ORDER,
""}};
static const ByteMnemonic call_jump_instr[] = {{0xE8, UNSET_OP_ORDER, "call"},
{0xE9, UNSET_OP_ORDER, "jmp"},
{-1, UNSET_OP_ORDER, ""}};
#define SHORT_IMMEDIATE_ENTRY(name, code) {code * 8 + 5, UNSET_OP_ORDER, #name},
static const ByteMnemonic short_immediate_instr[] = {
{0x05, UNSET_OP_ORDER, "add"}, {0x0D, UNSET_OP_ORDER, "or"},
{0x15, UNSET_OP_ORDER, "adc"}, {0x1D, UNSET_OP_ORDER, "sbb"},
{0x25, UNSET_OP_ORDER, "and"}, {0x2D, UNSET_OP_ORDER, "sub"},
{0x35, UNSET_OP_ORDER, "xor"}, {0x3D, UNSET_OP_ORDER, "cmp"},
{-1, UNSET_OP_ORDER, ""}};
X86_ALU_CODES(SHORT_IMMEDIATE_ENTRY){-1, UNSET_OP_ORDER, ""}};
static const char* const conditional_code_suffix[] = {
"o", "no", "c", "nc", "z", "nz", "na", "a",
"s", "ns", "pe", "po", "l", "ge", "le", "g"};
#define STRINGIFY(name, number) #name,
X86_CONDITIONAL_SUFFIXES(STRINGIFY)
#undef STRINGIFY
};
#define STRINGIFY_NAME(name, code) #name,
static const char* const xmm_conditional_code_suffix[] = {
XMM_CONDITIONAL_CODES(STRINGIFY_NAME)};
#undef STRINGIFY_NAME
enum InstructionType {
NO_INSTR,
@ -133,6 +104,18 @@ enum Prefixes {
REPEQ_PREFIX = REP_PREFIX
};
struct XmmMnemonic {
const char* ps_name;
const char* pd_name;
const char* ss_name;
const char* sd_name;
};
#define XMM_INSTRUCTION_ENTRY(name, code) \
{#name "ps", #name "pd", #name "ss", #name "sd"},
static const XmmMnemonic xmm_instructions[] = {
XMM_ALU_CODES(XMM_INSTRUCTION_ENTRY)};
struct InstructionDesc {
const char* mnem;
InstructionType type;
@ -605,7 +588,7 @@ void DisassemblerX64::PrintDisp(int disp, const char* after) {
// Returns number of bytes used by machine instruction, including *data byte.
// Writes immediate instructions to 'tmp_buffer_'.
int DisassemblerX64::PrintImmediateOp(uint8_t* data) {
bool byte_size_immediate = (*data & 0x02) != 0;
bool byte_size_immediate = (*data & 0x03) != 1;
uint8_t modrm = *(data + 1);
int mod, regop, rm;
get_modrm(modrm, &mod, &regop, &rm);
@ -1153,13 +1136,11 @@ bool DisassemblerX64::DecodeInstructionType(uint8_t** data) {
// REP.
Print("rep ");
}
// TODO(srdjan): Should we enable printing of REX.W?
// if (rex_w()) Print("REX.W ");
Print("%s%s", idesc.mnem, operand_size_code());
} else if (current == 0xC3 || current == 0xCC) {
Print("%s", idesc.mnem); // ret and int3 don't need a size specifier.
Print("%s", idesc.mnem);
} else if (current == 0x99 && rex_w()) {
Print("cqo"); // Cdql is called cdq and cdqq is called cqo.
} else {
Print("%s%s", idesc.mnem, operand_size_code());
Print("%s", idesc.mnem);
}
(*data)++;
break;
@ -1347,16 +1328,14 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
current += PrintRightXMMOperand(current);
} else {
const char* mnemonic = "?";
if (opcode == 0x14) {
if (opcode == 0x5A) {
mnemonic = "cvtpd2ps";
} else if (0x51 <= opcode && opcode <= 0x5F) {
mnemonic = xmm_instructions[opcode & 0xF].pd_name;
} else if (opcode == 0x14) {
mnemonic = "unpcklpd";
} else if (opcode == 0x15) {
mnemonic = "unpckhpd";
} else if (opcode == 0x54) {
mnemonic = "andpd";
} else if (opcode == 0x56) {
mnemonic = "orpd";
} else if (opcode == 0x57) {
mnemonic = "xorpd";
} else if (opcode == 0x2E) {
mnemonic = "ucomisd";
} else if (opcode == 0x2F) {
@ -1365,22 +1344,6 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
mnemonic = "paddd";
} else if (opcode == 0xFA) {
mnemonic = "psubd";
} else if (opcode == 0x58) {
mnemonic = "addpd";
} else if (opcode == 0x5C) {
mnemonic = "subpd";
} else if (opcode == 0x59) {
mnemonic = "mulpd";
} else if (opcode == 0x5E) {
mnemonic = "divpd";
} else if (opcode == 0x5D) {
mnemonic = "minpd";
} else if (opcode == 0x5F) {
mnemonic = "maxpd";
} else if (opcode == 0x51) {
mnemonic = "sqrtpd";
} else if (opcode == 0x5A) {
mnemonic = "cvtpd2ps";
} else if (opcode == 0xEF) {
mnemonic = "pxor";
} else {
@ -1424,10 +1387,12 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
get_modrm(*current, &mod, &regop, &rm);
Print("cvtsd2si%s %s,", operand_size_code(), NameOfCPURegister(regop));
current += PrintRightXMMOperand(current);
} else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
// XMM arithmetic. Mnemonic was retrieved at the start of this function.
} else if (0x51 <= opcode && opcode <= 0x5F) {
// XMM arithmetic. Get the F2 0F prefix version of the mnemonic.
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
const char* mnemonic =
opcode == 0x5A ? "cvtsd2ss" : xmm_instructions[opcode & 0xF].sd_name;
Print("%s %s,", mnemonic, NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
} else {
@ -1463,17 +1428,10 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
NameOfCPURegister(regop));
current += PrintRightXMMOperand(current);
} else if (0x51 <= opcode && opcode <= 0x5F) {
static const char* mnemonics[] = {"sqrtss", "rsqrtss", "rcpss", NULL,
NULL, NULL, NULL, "addss",
"mulss", "cvtss2sd", NULL, "subss",
"minss", "divss", "maxss"};
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
const char* mnemonic = mnemonics[opcode - 0x51];
if (mnemonic == NULL) {
UnimplementedInstruction();
mnemonic = "UNIMPLEMENTED";
}
const char* mnemonic =
opcode == 0x5A ? "cvtss2sd" : xmm_instructions[opcode & 0xF].ss_name;
Print("%s %s,", mnemonic, NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
} else if (opcode == 0x7E) {
@ -1553,18 +1511,10 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
Print("%s %s,", mnemonic, NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
} else if (0x51 <= opcode && opcode <= 0x5F) {
// ...ps xmm, xmm/m128
static const char* mnemonics[] = {"sqrtps", "rsqrtps", "rcpps", "andps",
NULL, "orps", "xorps", "addps",
"mulps", "cvtsd2ss", NULL, "subps",
"minps", "divps", "maxps"};
const char* mnemonic = mnemonics[opcode - 0x51];
if (mnemonic == NULL) {
UnimplementedInstruction();
mnemonic = "???";
}
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
const char* mnemonic =
opcode == 0x5A ? "cvtps2pd" : xmm_instructions[opcode & 0xF].ps_name;
Print("%s %s,", mnemonic, NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
} else if (opcode == 0xC2 || opcode == 0xC6) {
@ -1572,12 +1522,14 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
get_modrm(*current, &mod, &regop, &rm);
if (opcode == 0xC2) {
Print("cmpps %s,", NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
Print(" [%s]", xmm_conditional_code_suffix[*current]);
} else {
ASSERT(opcode == 0xC6);
Print("shufps %s,", NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
Print(" [%x]", *current);
}
current += PrintRightXMMOperand(current);
Print(" [%x]", *current);
current++;
} else if ((opcode & 0xF0) == 0x80) {
// Jcc: Conditional jump (branch).
@ -1625,12 +1577,10 @@ int DisassemblerX64::TwoByteOpcodeInstruction(uint8_t* data) {
// The argument is the second byte of the two-byte opcode.
// Returns NULL if the instruction is not handled here.
const char* DisassemblerX64::TwoByteMnemonic(uint8_t opcode) {
if (0x51 <= opcode && opcode <= 0x5F) {
static const char* mnemonics[] = {"sqrtsd", "rsqrtsd", "rcpsd", NULL,
NULL, NULL, NULL, "addsd",
"mulsd", NULL, NULL, "subsd",
"minsd", "divsd", "maxsd"};
return mnemonics[opcode - 0x51];
if (opcode == 0x5A) {
return "cvtps2pd";
} else if (0x51 <= opcode && opcode <= 0x5F) {
return xmm_instructions[opcode & 0xF].ps_name;
}
if (0xA2 <= opcode && opcode <= 0xBF) {
static const char* mnemonics[] = {
@ -1759,11 +1709,8 @@ int DisassemblerX64::InstructionDecode(uword pc) {
} break;
case 0x80: {
data++;
Print("cmpb ");
data += PrintRightByteOperand(data);
Print(",");
data += PrintImmediate(data, BYTE_SIZE);
byte_size_operand_ = true;
data += PrintImmediateOp(data);
} break;
case 0x88: // 8bit, fall through

View file

@ -3855,17 +3855,16 @@ DEFINE_EMIT(SimdBinaryOp,
SIMD_OP_FLOAT_ARITH(V, Sqrt, sqrt) \
SIMD_OP_FLOAT_ARITH(V, Negate, negate) \
SIMD_OP_FLOAT_ARITH(V, Abs, abs) \
V(Float32x4Reciprocal, reciprocalps) \
V(Float32x4Reciprocal, rcpps) \
V(Float32x4ReciprocalSqrt, rsqrtps)
DEFINE_EMIT(SimdUnaryOp, (SameAsFirstInput, XmmRegister value)) {
// TODO(dartbug.com/30949) select better register constraints to avoid
// redundant move of input into a different register because all instructions
// below support two operand forms.
// redundant move of input into a different register.
switch (instr->kind()) {
#define EMIT(Name, op) \
case SimdOpInstr::k##Name: \
__ op(value); \
__ op(value, value); \
break;
SIMD_OP_SIMPLE_UNARY(EMIT)
#undef EMIT
@ -4060,7 +4059,7 @@ DEFINE_EMIT(Int32x4Select,
// Copy mask.
__ movaps(temp, mask);
// Invert it.
__ notps(temp);
__ notps(temp, temp);
// mask = mask & trueValue.
__ andps(mask, trueValue);
// temp = temp & falseValue.
@ -4263,7 +4262,7 @@ LocationSummary* UnaryDoubleOpInstr::MakeLocationSummary(Zone* zone,
void UnaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
XmmRegister value = locs()->in(0).fpu_reg();
ASSERT(locs()->out(0).fpu_reg() == value);
__ DoubleNegate(value);
__ DoubleNegate(value, value);
}
LocationSummary* MathMinMaxInstr::MakeLocationSummary(Zone* zone,
@ -4350,7 +4349,7 @@ void MathMinMaxInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
if (is_min) {
__ cmovgeq(result, right);
} else {
__ cmovlessq(result, right);
__ cmovlq(result, right);
}
}

View file

@ -92,46 +92,6 @@ enum ScaleFactor {
TIMES_HALF_WORD_SIZE = kWordSizeLog2 - 1
};
enum Condition {
OVERFLOW = 0,
NO_OVERFLOW = 1,
BELOW = 2,
ABOVE_EQUAL = 3,
EQUAL = 4,
NOT_EQUAL = 5,
BELOW_EQUAL = 6,
ABOVE = 7,
SIGN = 8,
NOT_SIGN = 9,
PARITY_EVEN = 10,
PARITY_ODD = 11,
LESS = 12,
GREATER_EQUAL = 13,
LESS_EQUAL = 14,
GREATER = 15,
ZERO = EQUAL,
NOT_ZERO = NOT_EQUAL,
NEGATIVE = SIGN,
POSITIVE = NOT_SIGN,
CARRY = BELOW,
NOT_CARRY = ABOVE_EQUAL,
// Platform-independent variants declared for all platforms
// EQUAL,
// NOT_EQUAL,
// LESS,
// LESS_EQUAL,
// GREATER_EQUAL,
// GREATER,
UNSIGNED_LESS = BELOW,
UNSIGNED_LESS_EQUAL = BELOW_EQUAL,
UNSIGNED_GREATER = ABOVE,
UNSIGNED_GREATER_EQUAL = ABOVE_EQUAL,
INVALID_CONDITION = 16
};
class Instr {
public:
static const uint8_t kHltInstruction = 0xF4;

View file

@ -136,46 +136,6 @@ enum ScaleFactor {
TIMES_HALF_WORD_SIZE = kWordSizeLog2 - 1
};
enum Condition {
OVERFLOW = 0,
NO_OVERFLOW = 1,
BELOW = 2,
ABOVE_EQUAL = 3,
EQUAL = 4,
NOT_EQUAL = 5,
BELOW_EQUAL = 6,
ABOVE = 7,
SIGN = 8,
NOT_SIGN = 9,
PARITY_EVEN = 10,
PARITY_ODD = 11,
LESS = 12,
GREATER_EQUAL = 13,
LESS_EQUAL = 14,
GREATER = 15,
ZERO = EQUAL,
NOT_ZERO = NOT_EQUAL,
NEGATIVE = SIGN,
POSITIVE = NOT_SIGN,
CARRY = BELOW,
NOT_CARRY = ABOVE_EQUAL,
// Platform-independent variants declared for all platforms
// EQUAL,
// NOT_EQUAL,
// LESS,
// LESS_EQUAL,
// GREATER_EQUAL,
// GREATER,
UNSIGNED_LESS = BELOW,
UNSIGNED_LESS_EQUAL = BELOW_EQUAL,
UNSIGNED_GREATER = ABOVE,
UNSIGNED_GREATER_EQUAL = ABOVE_EQUAL,
INVALID_CONDITION = 16
};
#define R(reg) (1 << (reg))
#if defined(_WIN64)

132
runtime/vm/constants_x86.h Normal file
View file

@ -0,0 +1,132 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_CONSTANTS_X86_H_
#define RUNTIME_VM_CONSTANTS_X86_H_
namespace dart {
enum Condition {
OVERFLOW = 0,
NO_OVERFLOW = 1,
BELOW = 2,
ABOVE_EQUAL = 3,
EQUAL = 4,
NOT_EQUAL = 5,
BELOW_EQUAL = 6,
ABOVE = 7,
SIGN = 8,
NOT_SIGN = 9,
PARITY_EVEN = 10,
PARITY_ODD = 11,
LESS = 12,
GREATER_EQUAL = 13,
LESS_EQUAL = 14,
GREATER = 15,
ZERO = EQUAL,
NOT_ZERO = NOT_EQUAL,
NEGATIVE = SIGN,
POSITIVE = NOT_SIGN,
CARRY = BELOW,
NOT_CARRY = ABOVE_EQUAL,
// Platform-independent variants declared for all platforms
// EQUAL,
// NOT_EQUAL,
// LESS,
// LESS_EQUAL,
// GREATER_EQUAL,
// GREATER,
UNSIGNED_LESS = BELOW,
UNSIGNED_LESS_EQUAL = BELOW_EQUAL,
UNSIGNED_GREATER = ABOVE,
UNSIGNED_GREATER_EQUAL = ABOVE_EQUAL,
INVALID_CONDITION = 16
};
#define X86_ZERO_OPERAND_1_BYTE_INSTRUCTIONS(F) \
F(ret, 0xC3) \
F(leave, 0xC9) \
F(hlt, 0xF4) \
F(cld, 0xFC) \
F(int3, 0xCC) \
F(pushad, 0x60) \
F(popad, 0x61) \
F(pushfd, 0x9C) \
F(popfd, 0x9D) \
F(sahf, 0x9E) \
F(cdq, 0x99) \
F(fwait, 0x9B) \
F(movsb, 0xA4) \
F(movsl, 0xA5) \
F(cmpsb, 0xA6) \
F(cmpsl, 0xA7)
// clang-format off
#define X86_ALU_CODES(F) \
F(and, 4) \
F(or, 1) \
F(xor, 6) \
F(add, 0) \
F(adc, 2) \
F(sub, 5) \
F(sbb, 3) \
F(cmp, 7)
#define XMM_ALU_CODES(F) \
F(bad0, 0) \
F(sqrt, 1) \
F(rsqrt, 2) \
F(rcp, 3) \
F(and, 4) \
F(bad1, 5) \
F(or, 6) \
F(xor, 7) \
F(add, 8) \
F(mul, 9) \
F(bad2, 0xA) \
F(bad3, 0xB) \
F(sub, 0xC) \
F(min, 0xD) \
F(div, 0xE) \
F(max, 0xF)
// clang-format on
// Table 3-1, first part
#define XMM_CONDITIONAL_CODES(F) \
F(eq, 0) \
F(lt, 1) \
F(le, 2) \
F(unord, 3) \
F(neq, 4) \
F(nlt, 5) \
F(nle, 6) \
F(ord, 7)
#define X86_CONDITIONAL_SUFFIXES(F) \
F(o, OVERFLOW) \
F(no, NO_OVERFLOW) \
F(c, CARRY) \
F(nc, NOT_CARRY) \
F(z, ZERO) \
F(nz, NOT_ZERO) \
F(na, BELOW_EQUAL) \
F(a, ABOVE) \
F(s, SIGN) \
F(ns, NOT_SIGN) \
F(pe, PARITY_EVEN) \
F(po, PARITY_ODD) \
F(l, LESS) \
F(ge, GREATER_EQUAL) \
F(le, LESS_EQUAL) \
F(g, GREATER) \
/* Some alternative names */ \
F(e, EQUAL) \
F(ne, NOT_EQUAL)
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_X86_H_