diff --git a/assembler_ia32.cc b/assembler_ia32.cc index 6043f7fa5ff3..e7474f80c15c 100644 --- a/assembler_ia32.cc +++ b/assembler_ia32.cc @@ -309,6 +309,19 @@ void Assembler::rep_movsb() { EmitUint8(0xA4); } +void Assembler::rep_movsw() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x66); + EmitUint8(0xA5); +} + +void Assembler::rep_movsl() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0xA5); +} + void Assembler::movss(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); diff --git a/assembler_ia32.h b/assembler_ia32.h index 01cc4ebaea66..78d4c20ff931 100644 --- a/assembler_ia32.h +++ b/assembler_ia32.h @@ -298,6 +298,8 @@ class Assembler : public AssemblerBase { void cmovlessl(Register dst, Register src); void rep_movsb(); + void rep_movsw(); + void rep_movsl(); void movss(XmmRegister dst, const Address& src); void movss(const Address& dst, XmmRegister src); diff --git a/assembler_ia32_test.cc b/assembler_ia32_test.cc index 3bfccc07322a..f2557e7e5dd7 100644 --- a/assembler_ia32_test.cc +++ b/assembler_ia32_test.cc @@ -4755,14 +4755,16 @@ ASSEMBLER_TEST_GENERATE(TestRepMovsBytes, assembler) { } ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) { - const char* from = "0123456789"; - const char* to = new char[10]; - typedef void (*TestRepMovsBytes)(const char* from, const char* to, int count); + const char* from = "0123456789x"; + char* to = new char[11]; + to[10] = 'y'; + typedef void (*TestRepMovsBytes)(const char* from, char* to, int count); reinterpret_cast(test->entry())(from, to, 10); EXPECT_EQ(to[0], '0'); for (int i = 0; i < 10; i++) { EXPECT_EQ(from[i], to[i]); } + EXPECT_EQ(to[10], 'y'); delete[] to; EXPECT_DISASSEMBLY( "push esi\n" @@ -4778,6 +4780,93 @@ ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) { "ret\n"); } +ASSEMBLER_TEST_GENERATE(TestRepMovsWords, assembler) { + // Preserve registers. + __ pushl(ESI); + __ pushl(EDI); + __ pushl(ECX); + __ movl(ESI, Address(ESP, 4 * target::kWordSize)); // from. + __ movl(EDI, Address(ESP, 5 * target::kWordSize)); // to. + __ movl(ECX, Address(ESP, 6 * target::kWordSize)); // count. + __ rep_movsw(); + __ popl(ECX); + __ popl(EDI); + __ popl(ESI); + __ ret(); +} + +ASSEMBLER_TEST_RUN(TestRepMovsWords, test) { + const uint16_t from[11] = {0x0123, 0x1234, 0x2345, 0x3456, 0x4567, 0x5678, + 0x6789, 0x789A, 0x89AB, 0x9ABC, 0xABCD}; + uint16_t* to = new uint16_t[11]; + to[10] = 0xFEFE; + typedef void (*TestRepMovsWords)(const uint16_t* from, uint16_t* to, + int count); + reinterpret_cast(test->entry())(from, to, 10); + EXPECT_EQ(to[0], 0x0123u); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(from[i], to[i]); + } + EXPECT_EQ(to[10], 0xFEFEu); + delete[] to; + EXPECT_DISASSEMBLY( + "push esi\n" + "push edi\n" + "push ecx\n" + "mov esi,[esp+0x10]\n" + "mov edi,[esp+0x14]\n" + "mov ecx,[esp+0x18]\n" + "rep movsw\n" + "pop ecx\n" + "pop edi\n" + "pop esi\n" + "ret\n"); +} + +ASSEMBLER_TEST_GENERATE(TestRepMovsDwords, assembler) { + // Preserve registers. + __ pushl(ESI); + __ pushl(EDI); + __ pushl(ECX); + __ movl(ESI, Address(ESP, 4 * target::kWordSize)); // from. + __ movl(EDI, Address(ESP, 5 * target::kWordSize)); // to. + __ movl(ECX, Address(ESP, 6 * target::kWordSize)); // count. + __ rep_movsl(); + __ popl(ECX); + __ popl(EDI); + __ popl(ESI); + __ ret(); +} + +ASSEMBLER_TEST_RUN(TestRepMovsDwords, test) { + const uint32_t from[11] = {0x01234567, 0x12345678, 0x23456789, 0x3456789A, + 0x456789AB, 0x56789ABC, 0x6789ABCD, 0x789ABCDE, + 0x89ABCDEF, 0x9ABCDEF0, 0xABCDEF01}; + uint32_t* to = new uint32_t[11]; + to[10] = 0xFEFEFEFE; + typedef void (*TestRepMovsDwords)(const uint32_t* from, uint32_t* to, + int count); + reinterpret_cast(test->entry())(from, to, 10); + EXPECT_EQ(to[0], 0x01234567u); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(from[i], to[i]); + } + EXPECT_EQ(to[10], 0xFEFEFEFEu); + delete[] to; + EXPECT_DISASSEMBLY( + "push esi\n" + "push edi\n" + "push ecx\n" + "mov esi,[esp+0x10]\n" + "mov edi,[esp+0x14]\n" + "mov ecx,[esp+0x18]\n" + "rep movsl\n" + "pop ecx\n" + "pop edi\n" + "pop esi\n" + "ret\n"); +} + // Called from assembler_test.cc. ASSEMBLER_TEST_GENERATE(StoreIntoObject, assembler) { __ pushl(THR); diff --git a/assembler_x64.cc b/assembler_x64.cc index ef5a39789fd4..60d88ce5b2c6 100644 --- a/assembler_x64.cc +++ b/assembler_x64.cc @@ -384,11 +384,14 @@ void Assembler::movq(const Address& dst, const Immediate& imm) { } } -void Assembler::EmitSimple(int opcode, int opcode2) { +void Assembler::EmitSimple(int opcode, int opcode2, int opcode3) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(opcode); if (opcode2 != -1) { EmitUint8(opcode2); + if (opcode3 != -1) { + EmitUint8(opcode3); + } } } diff --git a/assembler_x64.h b/assembler_x64.h index 386f9c062187..622980ea0237 100644 --- a/assembler_x64.h +++ b/assembler_x64.h @@ -391,6 +391,9 @@ class Assembler : public AssemblerBase { SIMPLE(fsin, 0xD9, 0xFE) SIMPLE(lock, 0xF0) SIMPLE(rep_movsb, 0xF3, 0xA4) + SIMPLE(rep_movsw, 0xF3, 0x66, 0xA5) + SIMPLE(rep_movsl, 0xF3, 0xA5) + SIMPLE(rep_movsq, 0xF3, 0x48, 0xA5) #undef SIMPLE // XmmRegister operations with another register or an address. #define XX(width, name, ...) \ @@ -1030,7 +1033,7 @@ class Assembler : public AssemblerBase { const Address& dst, const Immediate& imm); - void EmitSimple(int opcode, int opcode2 = -1); + void EmitSimple(int opcode, int opcode2 = -1, int opcode3 = -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); diff --git a/assembler_x64_test.cc b/assembler_x64_test.cc index ef0619a71ad1..9080725578ed 100644 --- a/assembler_x64_test.cc +++ b/assembler_x64_test.cc @@ -5556,14 +5556,16 @@ ASSEMBLER_TEST_GENERATE(TestRepMovsBytes, assembler) { } ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) { - const char* from = "0123456789"; - const char* to = new char[10]; - typedef void (*TestRepMovsBytes)(const char* from, const char* to, int count); + const char* from = "0123456789x"; + char* to = new char[11]; + to[10] = 'y'; + typedef void (*TestRepMovsBytes)(const char* from, char* to, int count); reinterpret_cast(test->entry())(from, to, 10); EXPECT_EQ(to[0], '0'); for (int i = 0; i < 10; i++) { EXPECT_EQ(from[i], to[i]); } + EXPECT_EQ(to[10], 'y'); delete[] to; EXPECT_DISASSEMBLY_NOT_WINDOWS( "push rsi\n" @@ -5583,6 +5585,163 @@ ASSEMBLER_TEST_RUN(TestRepMovsBytes, test) { "ret\n"); } +ASSEMBLER_TEST_GENERATE(TestRepMovsWords, assembler) { + __ pushq(RSI); + __ pushq(RDI); + __ pushq(CallingConventions::kArg1Reg); // from. + __ pushq(CallingConventions::kArg2Reg); // to. + __ pushq(CallingConventions::kArg3Reg); // count. + __ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from. + __ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to. + __ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count. + __ rep_movsw(); + // Remove saved arguments. + __ popq(RAX); + __ popq(RAX); + __ popq(RAX); + __ popq(RDI); + __ popq(RSI); + __ ret(); +} + +ASSEMBLER_TEST_RUN(TestRepMovsWords, test) { + const uint16_t from[11] = {0x0123, 0x1234, 0x2345, 0x3456, 0x4567, 0x5678, + 0x6789, 0x789A, 0x89AB, 0x9ABC, 0xABCD}; + uint16_t* to = new uint16_t[11]; + to[10] = 0xFEFE; + typedef void (*TestRepMovsWords)(const uint16_t* from, uint16_t* to, + int count); + reinterpret_cast(test->entry())(from, to, 10); + EXPECT_EQ(to[0], 0x0123u); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(from[i], to[i]); + } + EXPECT_EQ(to[10], 0xFEFEu); + delete[] to; + EXPECT_DISASSEMBLY_NOT_WINDOWS( + "push rsi\n" + "push rdi\n" + "push rdi\n" + "push rsi\n" + "push rdx\n" + "movq rsi,[rsp+0x10]\n" + "movq rdi,[rsp+0x8]\n" + "movq rcx,[rsp]\n" + "rep movsw\n" + "pop rax\n" + "pop rax\n" + "pop rax\n" + "pop rdi\n" + "pop rsi\n" + "ret\n"); +} + +ASSEMBLER_TEST_GENERATE(TestRepMovsDwords, assembler) { + __ pushq(RSI); + __ pushq(RDI); + __ pushq(CallingConventions::kArg1Reg); // from. + __ pushq(CallingConventions::kArg2Reg); // to. + __ pushq(CallingConventions::kArg3Reg); // count. + __ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from. + __ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to. + __ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count. + __ rep_movsl(); + // Remove saved arguments. + __ popq(RAX); + __ popq(RAX); + __ popq(RAX); + __ popq(RDI); + __ popq(RSI); + __ ret(); +} + +ASSEMBLER_TEST_RUN(TestRepMovsDwords, test) { + const uint32_t from[11] = {0x01234567, 0x12345678, 0x23456789, 0x3456789A, + 0x456789AB, 0x56789ABC, 0x6789ABCD, 0x789ABCDE, + 0x89ABCDEF, 0x9ABCDEF0, 0xABCDEF01}; + uint32_t* to = new uint32_t[11]; + to[10] = 0xFEFEFEFE; + typedef void (*TestRepMovsDwords)(const uint32_t* from, uint32_t* to, + int count); + reinterpret_cast(test->entry())(from, to, 10); + EXPECT_EQ(to[0], 0x01234567u); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(from[i], to[i]); + } + EXPECT_EQ(to[10], 0xFEFEFEFEu); + delete[] to; + EXPECT_DISASSEMBLY_NOT_WINDOWS( + "push rsi\n" + "push rdi\n" + "push rdi\n" + "push rsi\n" + "push rdx\n" + "movq rsi,[rsp+0x10]\n" + "movq rdi,[rsp+0x8]\n" + "movq rcx,[rsp]\n" + "rep movsl\n" + "pop rax\n" + "pop rax\n" + "pop rax\n" + "pop rdi\n" + "pop rsi\n" + "ret\n"); +} + +ASSEMBLER_TEST_GENERATE(TestRepMovsQwords, assembler) { + __ pushq(RSI); + __ pushq(RDI); + __ pushq(CallingConventions::kArg1Reg); // from. + __ pushq(CallingConventions::kArg2Reg); // to. + __ pushq(CallingConventions::kArg3Reg); // count. + __ movq(RSI, Address(RSP, 2 * target::kWordSize)); // from. + __ movq(RDI, Address(RSP, 1 * target::kWordSize)); // to. + __ movq(RCX, Address(RSP, 0 * target::kWordSize)); // count. + __ rep_movsq(); + // Remove saved arguments. + __ popq(RAX); + __ popq(RAX); + __ popq(RAX); + __ popq(RDI); + __ popq(RSI); + __ ret(); +} + +ASSEMBLER_TEST_RUN(TestRepMovsQwords, test) { + const uint64_t from[11] = { + 0x0123456789ABCDEF, 0x123456789ABCDEF0, 0x23456789ABCDEF01, + 0x3456789ABCDEF012, 0x456789ABCDEF0123, 0x56789ABCDEF01234, + 0x6789ABCDEF012345, 0x789ABCDEF0123456, 0x89ABCDEF01234567, + 0x9ABCDEF012345678, 0xABCDEF0123456789}; + uint64_t* to = new uint64_t[11]; + to[10] = 0xFEFEFEFEFEFEFEFE; + typedef void (*TestRepMovsQwords)(const uint64_t* from, uint64_t* to, + int count); + reinterpret_cast(test->entry())(from, to, 10); + EXPECT_EQ(to[0], 0x0123456789ABCDEFu); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(from[i], to[i]); + } + EXPECT_EQ(to[10], 0xFEFEFEFEFEFEFEFEu); + delete[] to; + EXPECT_DISASSEMBLY_NOT_WINDOWS( + "push rsi\n" + "push rdi\n" + "push rdi\n" + "push rsi\n" + "push rdx\n" + "movq rsi,[rsp+0x10]\n" + "movq rdi,[rsp+0x8]\n" + "movq rcx,[rsp]\n" + "rep movsq\n" + "pop rax\n" + "pop rax\n" + "pop rax\n" + "pop rdi\n" + "pop rsi\n" + "ret\n"); +} + ASSEMBLER_TEST_GENERATE(ConditionalMovesCompare, assembler) { __ cmpq(CallingConventions::kArg1Reg, CallingConventions::kArg2Reg); __ movq(RDX, Immediate(1)); // Greater equal. diff --git a/disassembler_x86.cc b/disassembler_x86.cc index 53fdd69814f3..3cc671363741 100644 --- a/disassembler_x86.cc +++ b/disassembler_x86.cc @@ -1150,7 +1150,25 @@ bool DisassemblerX64::DecodeInstructionType(uint8_t** data) { // REP. Print("rep "); } - Print("%s", idesc.mnem); + if ((current & 0x01) == 0x01) { + // Operation size: word, dword or qword + switch (operand_size()) { + case WORD_SIZE: + Print("%sw", idesc.mnem); + break; + case DOUBLEWORD_SIZE: + Print("%sl", idesc.mnem); + break; + case QUADWORD_SIZE: + Print("%sq", idesc.mnem); + break; + default: + UNREACHABLE(); + } + } else { + // Operation size: byte + Print("%s", idesc.mnem); + } } else if (current == 0x99 && rex_w()) { Print("cqo"); // Cdql is called cdq and cdqq is called cqo. } else {