Skip to content

Commit

Permalink
Reland "[vm] MemoryCopy instruction for copying between typed data an…
Browse files Browse the repository at this point in the history
…d strings."

This is a reland of 6ecd8a1

Original change's description:
> [vm] MemoryCopy instruction for copying between typed data and strings.
> 
> Used for copying the bytes from the Uint8List to the _OneByteString in
> String.fromCharCodes and the pure-ASCII case of UTF-8 decoding.
> 
> Issue #42072
> Closes #41703
> 
> Change-Id: I1ae300222877d1c6e64e32c2f40b8fb187a584c0
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149500
> Commit-Queue: Aske Simon Christensen <askesc@google.com>
> Reviewed-by: Martin Kustermann <kustermann@google.com>

Change-Id: Ia231c521e5f2db168cfc6094dfc7322327dedc6d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150925
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
  • Loading branch information
askeksa authored and commit-bot@chromium.org committed Jun 11, 2020
1 parent 363ac11 commit e35ca30
Show file tree
Hide file tree
Showing 27 changed files with 938 additions and 55 deletions.
13 changes: 13 additions & 0 deletions runtime/vm/compiler/assembler/assembler_ia32.cc
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions runtime/vm/compiler/assembler/assembler_ia32.h
Expand Up @@ -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);
Expand Down
95 changes: 92 additions & 3 deletions runtime/vm/compiler/assembler/assembler_ia32_test.cc
Expand Up @@ -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<TestRepMovsBytes>(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"
Expand All @@ -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<TestRepMovsWords>(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<TestRepMovsDwords>(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);
Expand Down
5 changes: 4 additions & 1 deletion runtime/vm/compiler/assembler/assembler_x64.cc
Expand Up @@ -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);
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion runtime/vm/compiler/assembler/assembler_x64.h
Expand Up @@ -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, ...) \
Expand Down Expand Up @@ -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);
Expand Down
165 changes: 162 additions & 3 deletions runtime/vm/compiler/assembler/assembler_x64_test.cc
Expand Up @@ -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<TestRepMovsBytes>(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"
Expand All @@ -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<TestRepMovsWords>(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<TestRepMovsDwords>(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<TestRepMovsQwords>(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.
Expand Down
20 changes: 19 additions & 1 deletion runtime/vm/compiler/assembler/disassembler_x86.cc
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions runtime/vm/compiler/backend/constant_propagator.cc
Expand Up @@ -298,6 +298,8 @@ void ConstantPropagator::VisitStoreIndexed(StoreIndexedInstr* instr) {}
void ConstantPropagator::VisitStoreInstanceField(
StoreInstanceFieldInstr* instr) {}

void ConstantPropagator::VisitMemoryCopy(MemoryCopyInstr* instr) {}

void ConstantPropagator::VisitDeoptimize(DeoptimizeInstr* instr) {
// TODO(vegorov) remove all code after DeoptimizeInstr as dead.
}
Expand Down

0 comments on commit e35ca30

Please sign in to comment.