mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Reland "[vm] MemoryCopy instruction for copying between typed data and strings."
This is a reland of 6ecd8a10ea
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 https://github.com/dart-lang/sdk/issues/42072
> Closes https://github.com/dart-lang/sdk/issues/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>
This commit is contained in:
parent
363ac11c1f
commit
e35ca30ca5
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -377,6 +377,7 @@ struct InstrAttrs {
|
|||
M(NativeParameter, kNoGC) \
|
||||
M(LoadIndexedUnsafe, kNoGC) \
|
||||
M(StoreIndexedUnsafe, kNoGC) \
|
||||
M(MemoryCopy, kNoGC) \
|
||||
M(TailCall, kNoGC) \
|
||||
M(ParallelMove, kNoGC) \
|
||||
M(PushArgument, kNoGC) \
|
||||
|
@ -2691,6 +2692,86 @@ class LoadIndexedUnsafeInstr : public TemplateDefinition<1, NoThrow> {
|
|||
DISALLOW_COPY_AND_ASSIGN(LoadIndexedUnsafeInstr);
|
||||
};
|
||||
|
||||
class MemoryCopyInstr : public TemplateInstruction<5, NoThrow> {
|
||||
public:
|
||||
MemoryCopyInstr(Value* src,
|
||||
Value* dest,
|
||||
Value* src_start,
|
||||
Value* dest_start,
|
||||
Value* length,
|
||||
classid_t src_cid,
|
||||
classid_t dest_cid)
|
||||
: src_cid_(src_cid),
|
||||
dest_cid_(dest_cid),
|
||||
element_size_(Instance::ElementSizeFor(src_cid)) {
|
||||
ASSERT(IsArrayTypeSupported(src_cid));
|
||||
ASSERT(IsArrayTypeSupported(dest_cid));
|
||||
ASSERT(Instance::ElementSizeFor(src_cid) ==
|
||||
Instance::ElementSizeFor(dest_cid));
|
||||
SetInputAt(kSrcPos, src);
|
||||
SetInputAt(kDestPos, dest);
|
||||
SetInputAt(kSrcStartPos, src_start);
|
||||
SetInputAt(kDestStartPos, dest_start);
|
||||
SetInputAt(kLengthPos, length);
|
||||
}
|
||||
|
||||
enum {
|
||||
kSrcPos = 0,
|
||||
kDestPos = 1,
|
||||
kSrcStartPos = 2,
|
||||
kDestStartPos = 3,
|
||||
kLengthPos = 4
|
||||
};
|
||||
|
||||
DECLARE_INSTRUCTION(MemoryCopy)
|
||||
|
||||
virtual Representation RequiredInputRepresentation(intptr_t index) const {
|
||||
// All inputs are tagged (for now).
|
||||
return kTagged;
|
||||
}
|
||||
|
||||
virtual bool ComputeCanDeoptimize() const { return false; }
|
||||
virtual bool HasUnknownSideEffects() const { return true; }
|
||||
|
||||
virtual bool AttributesEqual(Instruction* other) const { return true; }
|
||||
|
||||
Value* src() const { return inputs_[kSrcPos]; }
|
||||
Value* dest() const { return inputs_[kDestPos]; }
|
||||
Value* src_start() const { return inputs_[kSrcStartPos]; }
|
||||
Value* dest_start() const { return inputs_[kDestStartPos]; }
|
||||
Value* length() const { return inputs_[kLengthPos]; }
|
||||
|
||||
private:
|
||||
// Set array_reg to point to the index indicated by start (contained in
|
||||
// start_reg) of the typed data or string in array (contained in array_reg).
|
||||
void EmitComputeStartPointer(FlowGraphCompiler* compiler,
|
||||
classid_t array_cid,
|
||||
Value* start,
|
||||
Register array_reg,
|
||||
Register start_reg);
|
||||
|
||||
static bool IsArrayTypeSupported(classid_t array_cid) {
|
||||
if (IsTypedDataBaseClassId(array_cid)) {
|
||||
return true;
|
||||
}
|
||||
switch (array_cid) {
|
||||
case kOneByteStringCid:
|
||||
case kTwoByteStringCid:
|
||||
case kExternalOneByteStringCid:
|
||||
case kExternalTwoByteStringCid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
classid_t src_cid_;
|
||||
classid_t dest_cid_;
|
||||
intptr_t element_size_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MemoryCopyInstr);
|
||||
};
|
||||
|
||||
// Unwinds the current frame and tail calls a target.
|
||||
//
|
||||
// The return address saved by the original caller of this frame will be in it's
|
||||
|
|
|
@ -154,6 +154,127 @@ DEFINE_BACKEND(TailCall,
|
|||
__ set_constant_pool_allowed(true);
|
||||
}
|
||||
|
||||
LocationSummary* MemoryCopyInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 5;
|
||||
const intptr_t kNumTemps =
|
||||
element_size_ == 16 ? 4 : element_size_ == 8 ? 2 : 1;
|
||||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
|
||||
locs->set_in(kSrcPos, Location::WritableRegister());
|
||||
locs->set_in(kDestPos, Location::WritableRegister());
|
||||
locs->set_in(kSrcStartPos, Location::RequiresRegister());
|
||||
locs->set_in(kDestStartPos, Location::RequiresRegister());
|
||||
locs->set_in(kLengthPos, Location::WritableRegister());
|
||||
for (intptr_t i = 0; i < kNumTemps; i++) {
|
||||
locs->set_temp(i, Location::RequiresRegister());
|
||||
}
|
||||
return locs;
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register src_reg = locs()->in(kSrcPos).reg();
|
||||
const Register dest_reg = locs()->in(kDestPos).reg();
|
||||
const Register src_start_reg = locs()->in(kSrcStartPos).reg();
|
||||
const Register dest_start_reg = locs()->in(kDestStartPos).reg();
|
||||
const Register length_reg = locs()->in(kLengthPos).reg();
|
||||
|
||||
const Register temp_reg = locs()->temp(0).reg();
|
||||
RegList temp_regs = 0;
|
||||
for (intptr_t i = 0; i < locs()->temp_count(); i++) {
|
||||
temp_regs |= 1 << locs()->temp(i).reg();
|
||||
}
|
||||
|
||||
EmitComputeStartPointer(compiler, src_cid_, src_start(), src_reg,
|
||||
src_start_reg);
|
||||
EmitComputeStartPointer(compiler, dest_cid_, dest_start(), dest_reg,
|
||||
dest_start_reg);
|
||||
|
||||
compiler::Label loop, done;
|
||||
|
||||
compiler::Address src_address =
|
||||
compiler::Address(src_reg, element_size_, compiler::Address::PostIndex);
|
||||
compiler::Address dest_address =
|
||||
compiler::Address(dest_reg, element_size_, compiler::Address::PostIndex);
|
||||
|
||||
// Untag length and skip copy if length is zero.
|
||||
__ movs(length_reg, compiler::Operand(length_reg, ASR, 1));
|
||||
__ b(&done, ZERO);
|
||||
|
||||
__ Bind(&loop);
|
||||
switch (element_size_) {
|
||||
case 1:
|
||||
__ ldrb(temp_reg, src_address);
|
||||
__ strb(temp_reg, dest_address);
|
||||
break;
|
||||
case 2:
|
||||
__ ldrh(temp_reg, src_address);
|
||||
__ strh(temp_reg, dest_address);
|
||||
break;
|
||||
case 4:
|
||||
__ ldr(temp_reg, src_address);
|
||||
__ str(temp_reg, dest_address);
|
||||
break;
|
||||
case 8:
|
||||
case 16:
|
||||
__ ldm(BlockAddressMode::IA_W, src_reg, temp_regs);
|
||||
__ stm(BlockAddressMode::IA_W, dest_reg, temp_regs);
|
||||
break;
|
||||
}
|
||||
__ subs(length_reg, length_reg, compiler::Operand(1));
|
||||
__ b(&loop, NOT_ZERO);
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitComputeStartPointer(FlowGraphCompiler* compiler,
|
||||
classid_t array_cid,
|
||||
Value* start,
|
||||
Register array_reg,
|
||||
Register start_reg) {
|
||||
if (IsTypedDataBaseClassId(array_cid)) {
|
||||
__ ldr(
|
||||
array_reg,
|
||||
compiler::FieldAddress(
|
||||
array_reg, compiler::target::TypedDataBase::data_field_offset()));
|
||||
} else {
|
||||
switch (array_cid) {
|
||||
case kOneByteStringCid:
|
||||
__ add(
|
||||
array_reg, array_reg,
|
||||
compiler::Operand(compiler::target::OneByteString::data_offset() -
|
||||
kHeapObjectTag));
|
||||
break;
|
||||
case kTwoByteStringCid:
|
||||
__ add(
|
||||
array_reg, array_reg,
|
||||
compiler::Operand(compiler::target::OneByteString::data_offset() -
|
||||
kHeapObjectTag));
|
||||
break;
|
||||
case kExternalOneByteStringCid:
|
||||
__ ldr(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalOneByteString::
|
||||
external_data_offset()));
|
||||
break;
|
||||
case kExternalTwoByteStringCid:
|
||||
__ ldr(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalTwoByteString::
|
||||
external_data_offset()));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
intptr_t shift = Utils::ShiftForPowerOfTwo(element_size_) - 1;
|
||||
if (shift < 0) {
|
||||
__ add(array_reg, array_reg, compiler::Operand(start_reg, ASR, -shift));
|
||||
} else {
|
||||
__ add(array_reg, array_reg, compiler::Operand(start_reg, LSL, shift));
|
||||
}
|
||||
}
|
||||
|
||||
LocationSummary* PushArgumentInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 1;
|
||||
|
|
|
@ -134,6 +134,134 @@ DEFINE_BACKEND(TailCall,
|
|||
__ set_constant_pool_allowed(true);
|
||||
}
|
||||
|
||||
LocationSummary* MemoryCopyInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 5;
|
||||
const intptr_t kNumTemps = 1;
|
||||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
|
||||
locs->set_in(kSrcPos, Location::WritableRegister());
|
||||
locs->set_in(kDestPos, Location::WritableRegister());
|
||||
locs->set_in(kSrcStartPos, Location::RequiresRegister());
|
||||
locs->set_in(kDestStartPos, Location::RequiresRegister());
|
||||
locs->set_in(kLengthPos, Location::WritableRegister());
|
||||
locs->set_temp(0, element_size_ == 16
|
||||
? Location::Pair(Location::RequiresRegister(),
|
||||
Location::RequiresRegister())
|
||||
: Location::RequiresRegister());
|
||||
return locs;
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register src_reg = locs()->in(kSrcPos).reg();
|
||||
const Register dest_reg = locs()->in(kDestPos).reg();
|
||||
const Register src_start_reg = locs()->in(kSrcStartPos).reg();
|
||||
const Register dest_start_reg = locs()->in(kDestStartPos).reg();
|
||||
const Register length_reg = locs()->in(kLengthPos).reg();
|
||||
|
||||
Register temp_reg, temp_reg2;
|
||||
if (locs()->temp(0).IsPairLocation()) {
|
||||
PairLocation* pair = locs()->temp(0).AsPairLocation();
|
||||
temp_reg = pair->At(0).reg();
|
||||
temp_reg2 = pair->At(1).reg();
|
||||
} else {
|
||||
temp_reg = locs()->temp(0).reg();
|
||||
temp_reg2 = kNoRegister;
|
||||
}
|
||||
|
||||
EmitComputeStartPointer(compiler, src_cid_, src_start(), src_reg,
|
||||
src_start_reg);
|
||||
EmitComputeStartPointer(compiler, dest_cid_, dest_start(), dest_reg,
|
||||
dest_start_reg);
|
||||
|
||||
compiler::Label loop, done;
|
||||
|
||||
compiler::Address src_address =
|
||||
compiler::Address(src_reg, element_size_, compiler::Address::PostIndex);
|
||||
compiler::Address dest_address =
|
||||
compiler::Address(dest_reg, element_size_, compiler::Address::PostIndex);
|
||||
|
||||
// Untag length and skip copy if length is zero.
|
||||
__ adds(length_reg, ZR, compiler::Operand(length_reg, ASR, 1));
|
||||
__ b(&done, ZERO);
|
||||
|
||||
__ Bind(&loop);
|
||||
switch (element_size_) {
|
||||
case 1:
|
||||
__ ldr(temp_reg, src_address, kUnsignedByte);
|
||||
__ str(temp_reg, dest_address, kUnsignedByte);
|
||||
break;
|
||||
case 2:
|
||||
__ ldr(temp_reg, src_address, kUnsignedHalfword);
|
||||
__ str(temp_reg, dest_address, kUnsignedHalfword);
|
||||
break;
|
||||
case 4:
|
||||
__ ldr(temp_reg, src_address, kUnsignedWord);
|
||||
__ str(temp_reg, dest_address, kUnsignedWord);
|
||||
break;
|
||||
case 8:
|
||||
__ ldr(temp_reg, src_address, kDoubleWord);
|
||||
__ str(temp_reg, dest_address, kDoubleWord);
|
||||
break;
|
||||
case 16:
|
||||
__ ldp(temp_reg, temp_reg2, src_address, kDoubleWord);
|
||||
__ stp(temp_reg, temp_reg2, dest_address, kDoubleWord);
|
||||
break;
|
||||
}
|
||||
__ subs(length_reg, length_reg, compiler::Operand(1));
|
||||
__ b(&loop, NOT_ZERO);
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitComputeStartPointer(FlowGraphCompiler* compiler,
|
||||
classid_t array_cid,
|
||||
Value* start,
|
||||
Register array_reg,
|
||||
Register start_reg) {
|
||||
if (IsTypedDataBaseClassId(array_cid)) {
|
||||
__ ldr(
|
||||
array_reg,
|
||||
compiler::FieldAddress(
|
||||
array_reg, compiler::target::TypedDataBase::data_field_offset()));
|
||||
} else {
|
||||
switch (array_cid) {
|
||||
case kOneByteStringCid:
|
||||
__ add(
|
||||
array_reg, array_reg,
|
||||
compiler::Operand(compiler::target::OneByteString::data_offset() -
|
||||
kHeapObjectTag));
|
||||
break;
|
||||
case kTwoByteStringCid:
|
||||
__ add(
|
||||
array_reg, array_reg,
|
||||
compiler::Operand(compiler::target::OneByteString::data_offset() -
|
||||
kHeapObjectTag));
|
||||
break;
|
||||
case kExternalOneByteStringCid:
|
||||
__ ldr(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalOneByteString::
|
||||
external_data_offset()));
|
||||
break;
|
||||
case kExternalTwoByteStringCid:
|
||||
__ ldr(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalTwoByteString::
|
||||
external_data_offset()));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
intptr_t shift = Utils::ShiftForPowerOfTwo(element_size_) - 1;
|
||||
if (shift < 0) {
|
||||
__ add(array_reg, array_reg, compiler::Operand(start_reg, ASR, -shift));
|
||||
} else {
|
||||
__ add(array_reg, array_reg, compiler::Operand(start_reg, LSL, shift));
|
||||
}
|
||||
}
|
||||
|
||||
LocationSummary* PushArgumentInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 1;
|
||||
|
|
|
@ -75,6 +75,121 @@ DEFINE_BACKEND(TailCall,
|
|||
__ jmp(temp);
|
||||
}
|
||||
|
||||
LocationSummary* MemoryCopyInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 5;
|
||||
const intptr_t kNumTemps = 0;
|
||||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
|
||||
locs->set_in(kSrcPos, Location::RequiresRegister());
|
||||
locs->set_in(kDestPos, Location::RegisterLocation(EDI));
|
||||
locs->set_in(kSrcStartPos, Location::WritableRegister());
|
||||
locs->set_in(kDestStartPos, Location::WritableRegister());
|
||||
locs->set_in(kLengthPos, Location::RegisterLocation(ECX));
|
||||
return locs;
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register src_reg = locs()->in(kSrcPos).reg();
|
||||
const Register src_start_reg = locs()->in(kSrcStartPos).reg();
|
||||
const Register dest_start_reg = locs()->in(kDestStartPos).reg();
|
||||
|
||||
// Save ESI which is THR.
|
||||
__ pushl(ESI);
|
||||
__ movl(ESI, src_reg);
|
||||
|
||||
EmitComputeStartPointer(compiler, src_cid_, src_start(), ESI, src_start_reg);
|
||||
EmitComputeStartPointer(compiler, dest_cid_, dest_start(), EDI,
|
||||
dest_start_reg);
|
||||
if (element_size_ <= 4) {
|
||||
__ SmiUntag(ECX);
|
||||
} else if (element_size_ == 16) {
|
||||
__ shll(ECX, compiler::Immediate(1));
|
||||
}
|
||||
switch (element_size_) {
|
||||
case 1:
|
||||
__ rep_movsb();
|
||||
break;
|
||||
case 2:
|
||||
__ rep_movsw();
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
__ rep_movsl();
|
||||
break;
|
||||
}
|
||||
|
||||
// Restore THR.
|
||||
__ popl(ESI);
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitComputeStartPointer(FlowGraphCompiler* compiler,
|
||||
classid_t array_cid,
|
||||
Value* start,
|
||||
Register array_reg,
|
||||
Register start_reg) {
|
||||
intptr_t offset;
|
||||
if (IsTypedDataBaseClassId(array_cid)) {
|
||||
__ movl(
|
||||
array_reg,
|
||||
compiler::FieldAddress(
|
||||
array_reg, compiler::target::TypedDataBase::data_field_offset()));
|
||||
offset = 0;
|
||||
} else {
|
||||
switch (array_cid) {
|
||||
case kOneByteStringCid:
|
||||
offset =
|
||||
compiler::target::OneByteString::data_offset() - kHeapObjectTag;
|
||||
break;
|
||||
case kTwoByteStringCid:
|
||||
offset =
|
||||
compiler::target::TwoByteString::data_offset() - kHeapObjectTag;
|
||||
break;
|
||||
case kExternalOneByteStringCid:
|
||||
__ movl(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalOneByteString::
|
||||
external_data_offset()));
|
||||
offset = 0;
|
||||
break;
|
||||
case kExternalTwoByteStringCid:
|
||||
__ movl(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalTwoByteString::
|
||||
external_data_offset()));
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ScaleFactor scale;
|
||||
switch (element_size_) {
|
||||
case 1:
|
||||
__ SmiUntag(start_reg);
|
||||
scale = TIMES_1;
|
||||
break;
|
||||
case 2:
|
||||
scale = TIMES_1;
|
||||
break;
|
||||
case 4:
|
||||
scale = TIMES_2;
|
||||
break;
|
||||
case 8:
|
||||
scale = TIMES_4;
|
||||
break;
|
||||
case 16:
|
||||
scale = TIMES_8;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
__ leal(array_reg, compiler::Address(array_reg, start_reg, scale, offset));
|
||||
}
|
||||
|
||||
LocationSummary* PushArgumentInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 1;
|
||||
|
|
|
@ -127,6 +127,113 @@ DEFINE_BACKEND(TailCall, (NoLocation, Fixed<Register, ARGS_DESC_REG>)) {
|
|||
__ set_constant_pool_allowed(true);
|
||||
}
|
||||
|
||||
LocationSummary* MemoryCopyInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 5;
|
||||
const intptr_t kNumTemps = 0;
|
||||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
|
||||
locs->set_in(kSrcPos, Location::RegisterLocation(RSI));
|
||||
locs->set_in(kDestPos, Location::RegisterLocation(RDI));
|
||||
locs->set_in(kSrcStartPos, Location::WritableRegister());
|
||||
locs->set_in(kDestStartPos, Location::WritableRegister());
|
||||
locs->set_in(kLengthPos, Location::RegisterLocation(RCX));
|
||||
return locs;
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register src_start_reg = locs()->in(kSrcStartPos).reg();
|
||||
const Register dest_start_reg = locs()->in(kDestStartPos).reg();
|
||||
|
||||
EmitComputeStartPointer(compiler, src_cid_, src_start(), RSI, src_start_reg);
|
||||
EmitComputeStartPointer(compiler, dest_cid_, dest_start(), RDI,
|
||||
dest_start_reg);
|
||||
if (element_size_ <= 8) {
|
||||
__ SmiUntag(RCX);
|
||||
}
|
||||
switch (element_size_) {
|
||||
case 1:
|
||||
__ rep_movsb();
|
||||
break;
|
||||
case 2:
|
||||
__ rep_movsw();
|
||||
break;
|
||||
case 4:
|
||||
__ rep_movsl();
|
||||
break;
|
||||
case 8:
|
||||
case 16:
|
||||
__ rep_movsq();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryCopyInstr::EmitComputeStartPointer(FlowGraphCompiler* compiler,
|
||||
classid_t array_cid,
|
||||
Value* start,
|
||||
Register array_reg,
|
||||
Register start_reg) {
|
||||
intptr_t offset;
|
||||
if (IsTypedDataBaseClassId(array_cid)) {
|
||||
__ movq(
|
||||
array_reg,
|
||||
compiler::FieldAddress(
|
||||
array_reg, compiler::target::TypedDataBase::data_field_offset()));
|
||||
offset = 0;
|
||||
} else {
|
||||
switch (array_cid) {
|
||||
case kOneByteStringCid:
|
||||
offset =
|
||||
compiler::target::OneByteString::data_offset() - kHeapObjectTag;
|
||||
break;
|
||||
case kTwoByteStringCid:
|
||||
offset =
|
||||
compiler::target::TwoByteString::data_offset() - kHeapObjectTag;
|
||||
break;
|
||||
case kExternalOneByteStringCid:
|
||||
__ movq(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalOneByteString::
|
||||
external_data_offset()));
|
||||
offset = 0;
|
||||
break;
|
||||
case kExternalTwoByteStringCid:
|
||||
__ movq(array_reg,
|
||||
compiler::FieldAddress(array_reg,
|
||||
compiler::target::ExternalTwoByteString::
|
||||
external_data_offset()));
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ScaleFactor scale;
|
||||
switch (element_size_) {
|
||||
case 1:
|
||||
__ SmiUntag(start_reg);
|
||||
scale = TIMES_1;
|
||||
break;
|
||||
case 2:
|
||||
scale = TIMES_1;
|
||||
break;
|
||||
case 4:
|
||||
scale = TIMES_2;
|
||||
break;
|
||||
case 8:
|
||||
scale = TIMES_4;
|
||||
break;
|
||||
case 16:
|
||||
scale = TIMES_8;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
__ leaq(array_reg, compiler::Address(array_reg, start_reg, scale, offset));
|
||||
}
|
||||
|
||||
LocationSummary* PushArgumentInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
const intptr_t kNumInputs = 1;
|
||||
|
|
|
@ -245,6 +245,18 @@ Fragment BaseFlowGraphBuilder::IntConstant(int64_t value) {
|
|||
Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld))));
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::MemoryCopy(classid_t src_cid,
|
||||
classid_t dest_cid) {
|
||||
Value* length = Pop();
|
||||
Value* dest_start = Pop();
|
||||
Value* src_start = Pop();
|
||||
Value* dest = Pop();
|
||||
Value* src = Pop();
|
||||
auto copy = new (Z) MemoryCopyInstr(src, dest, src_start, dest_start, length,
|
||||
src_cid, dest_cid);
|
||||
return Fragment(copy);
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::TailCall(const Code& code) {
|
||||
Value* arg_desc = Pop();
|
||||
return Fragment(new (Z) TailCallInstr(code, arg_desc));
|
||||
|
|
|
@ -285,6 +285,7 @@ class BaseFlowGraphBuilder {
|
|||
intptr_t stack_depth,
|
||||
intptr_t loop_depth);
|
||||
Fragment CheckStackOverflowInPrologue(TokenPosition position);
|
||||
Fragment MemoryCopy(classid_t src_cid, classid_t dest_cid);
|
||||
Fragment TailCall(const Code& code);
|
||||
Fragment Utf8Scan();
|
||||
|
||||
|
|
|
@ -831,6 +831,7 @@ bool FlowGraphBuilder::IsRecognizedMethodForFlowGraph(
|
|||
case MethodRecognizer::kGrowableArrayCapacity:
|
||||
case MethodRecognizer::kListFactory:
|
||||
case MethodRecognizer::kObjectArrayAllocate:
|
||||
case MethodRecognizer::kCopyRangeFromUint8ListToOneByteString:
|
||||
case MethodRecognizer::kLinkedHashMap_getIndex:
|
||||
case MethodRecognizer::kLinkedHashMap_setIndex:
|
||||
case MethodRecognizer::kLinkedHashMap_getData:
|
||||
|
@ -1055,6 +1056,16 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
|||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += CreateArray();
|
||||
break;
|
||||
case MethodRecognizer::kCopyRangeFromUint8ListToOneByteString:
|
||||
ASSERT(function.NumParameters() == 5);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(2));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(3));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(4));
|
||||
body += MemoryCopy(kTypedDataUint8ArrayCid, kOneByteStringCid);
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getIndex:
|
||||
ASSERT(function.NumParameters() == 1);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
|
|
|
@ -65,6 +65,8 @@ namespace dart {
|
|||
V(_Int32x4ArrayView, ._, TypedData_Int32x4ArrayView_factory, 0x9bfbd6d5) \
|
||||
V(_Float64x2ArrayView, ._, TypedData_Float64x2ArrayView_factory, 0x1a383408) \
|
||||
V(::, _toClampedUint8, ConvertIntToClampedUint8, 0x59765a4a) \
|
||||
V(::, copyRangeFromUint8ListToOneByteString, \
|
||||
CopyRangeFromUint8ListToOneByteString, 0x00000000) \
|
||||
V(_StringBase, _interpolate, StringBaseInterpolate, 0xc0a650e4) \
|
||||
V(_IntegerImplementation, toDouble, IntegerToDouble, 0x22a26db3) \
|
||||
V(_Double, _add, DoubleAdd, 0x2f5c036a) \
|
||||
|
|
|
@ -76,9 +76,9 @@ static inline Condition InvertCondition(Condition c) {
|
|||
F(cdq, 0x99) \
|
||||
F(fwait, 0x9B) \
|
||||
F(movsb, 0xA4) \
|
||||
F(movsl, 0xA5) \
|
||||
F(movs, 0xA5) /* Size suffix added in code */ \
|
||||
F(cmpsb, 0xA6) \
|
||||
F(cmpsl, 0xA7)
|
||||
F(cmps, 0xA7) /* Size suffix added in code */
|
||||
|
||||
// clang-format off
|
||||
#define X86_ALU_CODES(F) \
|
||||
|
|
|
@ -14,6 +14,7 @@ import "dart:_internal"
|
|||
allocateOneByteString,
|
||||
allocateTwoByteString,
|
||||
ClassID,
|
||||
copyRangeFromUint8ListToOneByteString,
|
||||
patch,
|
||||
POWERS_OF_TEN,
|
||||
unsafeCast,
|
||||
|
@ -1751,10 +1752,9 @@ class _Utf8Decoder {
|
|||
if (flags == 0) {
|
||||
// Pure ASCII.
|
||||
assert(size == end - start);
|
||||
// TODO(dartbug.com/41703): String.fromCharCodes has a lot of overhead
|
||||
// checking types and ranges, which is redundant in this case. Find a
|
||||
// more direct way to do the conversion.
|
||||
return String.fromCharCodes(bytes, start, end);
|
||||
String result = allocateOneByteString(size);
|
||||
copyRangeFromUint8ListToOneByteString(bytes, result, start, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
String result;
|
||||
|
@ -1846,10 +1846,9 @@ class _Utf8Decoder {
|
|||
// Pure ASCII.
|
||||
assert(_state == accept);
|
||||
assert(size == end - start);
|
||||
// TODO(dartbug.com/41703): String.fromCharCodes has a lot of overhead
|
||||
// checking types and ranges, which is redundant in this case. Find a
|
||||
// more direct way to do the conversion.
|
||||
return String.fromCharCodes(bytes, start, end);
|
||||
String result = allocateOneByteString(size);
|
||||
copyRangeFromUint8ListToOneByteString(bytes, result, start, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Do not include any final, incomplete character in size.
|
||||
|
@ -1932,8 +1931,6 @@ class _Utf8Decoder {
|
|||
|
||||
String decode8(Uint8List bytes, int start, int end, int size) {
|
||||
assert(start < end);
|
||||
// TODO(dartbug.com/41704): Allocate an uninitialized _OneByteString and
|
||||
// write characters to it using _setAt.
|
||||
final String result = allocateOneByteString(size);
|
||||
int i = start;
|
||||
int j = 0;
|
||||
|
@ -1986,8 +1983,6 @@ class _Utf8Decoder {
|
|||
assert(start < end);
|
||||
final String typeTable = _Utf8Decoder.typeTable;
|
||||
final String transitionTable = _Utf8Decoder.transitionTable;
|
||||
// TODO(dartbug.com/41704): Allocate an uninitialized _TwoByteString and
|
||||
// write characters to it using _setAt.
|
||||
final String result = allocateTwoByteString(size);
|
||||
int i = start;
|
||||
int j = 0;
|
||||
|
|
|
@ -17,6 +17,7 @@ import "dart:_internal"
|
|||
allocateTwoByteString,
|
||||
ClassID,
|
||||
CodeUnits,
|
||||
copyRangeFromUint8ListToOneByteString,
|
||||
EfficientLengthIterable,
|
||||
FixedLengthListBase,
|
||||
IterableElementError,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
import "dart:core" hide Symbol;
|
||||
|
||||
import "dart:isolate" show SendPort;
|
||||
import "dart:typed_data" show Int32List;
|
||||
import "dart:typed_data" show Int32List, Uint8List;
|
||||
|
||||
/// These are the additional parts of this patch library:
|
||||
// part "class_id_fasta.dart";
|
||||
|
@ -41,6 +41,18 @@ String allocateOneByteString(int length)
|
|||
void writeIntoOneByteString(String string, int index, int codePoint)
|
||||
native "Internal_writeIntoOneByteString";
|
||||
|
||||
/// This function is recognized by the VM and compiled into specialized code.
|
||||
/// It is assumed that [from] is a native [Uint8List] class and [to] is a
|
||||
/// [_OneByteString]. The [fromStart] and [toStart] indices together with the
|
||||
/// [length] must specify ranges within the bounds of the list / string.
|
||||
@pragma("vm:prefer-inline")
|
||||
void copyRangeFromUint8ListToOneByteString(
|
||||
Uint8List from, String to, int fromStart, int toStart, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
writeIntoOneByteString(to, toStart + i, from[fromStart + i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// The returned string is a [_TwoByteString] with uninitialized content.
|
||||
@pragma("vm:entry-point", "call")
|
||||
String allocateTwoByteString(int length)
|
||||
|
|
|
@ -224,14 +224,14 @@ abstract class _StringBase implements String {
|
|||
// It's always faster to do this in Dart than to call into the runtime.
|
||||
var s = _OneByteString._allocate(len);
|
||||
|
||||
// Special case for _Uint8ArrayView.
|
||||
if (charCodes is Uint8List) {
|
||||
if (start >= 0 && len >= 0) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
s._setAt(i, charCodes[start + i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
// Special case for native Uint8 typed arrays.
|
||||
final int cid = ClassID.getID(charCodes);
|
||||
if (cid == ClassID.cidUint8ArrayView ||
|
||||
cid == ClassID.cidUint8Array ||
|
||||
cid == ClassID.cidExternalUint8Array) {
|
||||
Uint8List bytes = unsafeCast<Uint8List>(charCodes);
|
||||
copyRangeFromUint8ListToOneByteString(bytes, s, start, 0, len);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Fall through to normal case.
|
||||
|
|
|
@ -12,6 +12,7 @@ import "dart:_internal"
|
|||
allocateOneByteString,
|
||||
allocateTwoByteString,
|
||||
ClassID,
|
||||
copyRangeFromUint8ListToOneByteString,
|
||||
patch,
|
||||
POWERS_OF_TEN,
|
||||
unsafeCast,
|
||||
|
@ -1755,10 +1756,9 @@ class _Utf8Decoder {
|
|||
if (flags == 0) {
|
||||
// Pure ASCII.
|
||||
assert(size == end - start);
|
||||
// TODO(dartbug.com/41703): String.fromCharCodes has a lot of overhead
|
||||
// checking types and ranges, which is redundant in this case. Find a
|
||||
// more direct way to do the conversion.
|
||||
return String.fromCharCodes(bytes, start, end);
|
||||
String result = allocateOneByteString(size);
|
||||
copyRangeFromUint8ListToOneByteString(bytes, result, start, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
String result;
|
||||
|
@ -1850,10 +1850,9 @@ class _Utf8Decoder {
|
|||
// Pure ASCII.
|
||||
assert(_state == accept);
|
||||
assert(size == end - start);
|
||||
// TODO(dartbug.com/41703): String.fromCharCodes has a lot of overhead
|
||||
// checking types and ranges, which is redundant in this case. Find a
|
||||
// more direct way to do the conversion.
|
||||
return String.fromCharCodes(bytes, start, end);
|
||||
String result = allocateOneByteString(size);
|
||||
copyRangeFromUint8ListToOneByteString(bytes, result, start, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Do not include any final, incomplete character in size.
|
||||
|
@ -1936,8 +1935,6 @@ class _Utf8Decoder {
|
|||
|
||||
String decode8(Uint8List bytes, int start, int end, int size) {
|
||||
assert(start < end);
|
||||
// TODO(dartbug.com/41704): Allocate an uninitialized _OneByteString and
|
||||
// write characters to it using _setAt.
|
||||
String result = allocateOneByteString(size);
|
||||
int i = start;
|
||||
int j = 0;
|
||||
|
@ -1990,8 +1987,6 @@ class _Utf8Decoder {
|
|||
assert(start < end);
|
||||
final String typeTable = _Utf8Decoder.typeTable;
|
||||
final String transitionTable = _Utf8Decoder.transitionTable;
|
||||
// TODO(dartbug.com/41704): Allocate an uninitialized _TwoByteString and
|
||||
// write characters to it using _setAt.
|
||||
String result = allocateTwoByteString(size);
|
||||
int i = start;
|
||||
int j = 0;
|
||||
|
|
|
@ -15,6 +15,7 @@ import "dart:_internal"
|
|||
allocateTwoByteString,
|
||||
ClassID,
|
||||
CodeUnits,
|
||||
copyRangeFromUint8ListToOneByteString,
|
||||
EfficientLengthIterable,
|
||||
FixedLengthListBase,
|
||||
IterableElementError,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
import "dart:core" hide Symbol;
|
||||
|
||||
import "dart:isolate" show SendPort;
|
||||
import "dart:typed_data" show Int32List;
|
||||
import "dart:typed_data" show Int32List, Uint8List;
|
||||
|
||||
/// These are the additional parts of this patch library:
|
||||
// part "class_id_fasta.dart";
|
||||
|
@ -45,6 +45,18 @@ String allocateOneByteString(int length)
|
|||
void writeIntoOneByteString(String string, int index, int codePoint)
|
||||
native "Internal_writeIntoOneByteString";
|
||||
|
||||
/// This function is recognized by the VM and compiled into specialized code.
|
||||
/// It is assumed that [from] is a native [Uint8List] class and [to] is a
|
||||
/// [_OneByteString]. The [fromStart] and [toStart] indices together with the
|
||||
/// [length] must specify ranges within the bounds of the list / string.
|
||||
@pragma("vm:prefer-inline")
|
||||
void copyRangeFromUint8ListToOneByteString(
|
||||
Uint8List from, String to, int fromStart, int toStart, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
writeIntoOneByteString(to, toStart + i, from[fromStart + i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// The returned string is a [_TwoByteString] with uninitialized content.
|
||||
@pragma("vm:entry-point", "call")
|
||||
String allocateTwoByteString(int length)
|
||||
|
|
|
@ -229,14 +229,14 @@ abstract class _StringBase implements String {
|
|||
// It's always faster to do this in Dart than to call into the runtime.
|
||||
var s = _OneByteString._allocate(len);
|
||||
|
||||
// Special case for _Uint8ArrayView.
|
||||
if (charCodes is Uint8List) {
|
||||
if (start >= 0 && len >= 0) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
s._setAt(i, charCodes[start + i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
// Special case for native Uint8 typed arrays.
|
||||
final int cid = ClassID.getID(charCodes);
|
||||
if (cid == ClassID.cidUint8ArrayView ||
|
||||
cid == ClassID.cidUint8Array ||
|
||||
cid == ClassID.cidExternalUint8Array) {
|
||||
Uint8List bytes = unsafeCast<Uint8List>(charCodes);
|
||||
copyRangeFromUint8ListToOneByteString(bytes, s, start, 0, len);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Fall through to normal case.
|
||||
|
|
|
@ -35,5 +35,5 @@ MINOR 9
|
|||
PATCH 0
|
||||
PRERELEASE 0
|
||||
PRERELEASE_PATCH 0
|
||||
ABI_VERSION 35
|
||||
OLDEST_SUPPORTED_ABI_VERSION 35
|
||||
ABI_VERSION 36
|
||||
OLDEST_SUPPORTED_ABI_VERSION 36
|
||||
|
|
Loading…
Reference in a new issue