[vm, compiler] Remove use of StoreIntoObject for arrays from hand-written assembly.

- Convert _List.[]= and _List._setIndexed to graph intrinsics.
 - Remove _GrowableList.add intrinsics (dead since Dart 2).

Eliminates need to shuffle registers at the assembler level when introducing card-remembering.

Change-Id: Ia94ae3229a5eb90215ac74ab624db843a0cda01c
Reviewed-on: https://dart-review.googlesource.com/c/82380
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak 2018-11-01 16:30:28 +00:00 committed by commit-bot@chromium.org
parent cd44af1a14
commit 97940e77a4
6 changed files with 27 additions and 224 deletions

View file

@ -689,7 +689,7 @@ static bool IntrinsifyArraySetIndexed(FlowGraph* flow_graph,
MethodRecognizer::k##enum_name##SetIndexed)); \
}
DEFINE_ARRAY_GETTER_INTRINSIC(ObjectArray) // Setter in intrinsifier_<arch>.cc.
DEFINE_ARRAY_GETTER_INTRINSIC(ObjectArray)
DEFINE_ARRAY_GETTER_INTRINSIC(ImmutableArray)
#define DEFINE_ARRAY_GETTER_SETTER_INTRINSICS(enum_name) \
@ -979,13 +979,33 @@ bool Intrinsifier::Build_GrowableArrayGetIndexed(FlowGraph* flow_graph) {
return true;
}
void Intrinsifier::ObjectArraySetIndexed(Assembler* assembler,
Label* normal_ir_body) {
bool Intrinsifier::Build_ObjectArraySetIndexed(FlowGraph* flow_graph) {
if (Isolate::Current()->argument_type_checks()) {
return;
return false;
}
ObjectArraySetIndexedUnchecked(assembler, normal_ir_body);
return Build_ObjectArraySetIndexedUnchecked(flow_graph);
}
bool Intrinsifier::Build_ObjectArraySetIndexedUnchecked(FlowGraph* flow_graph) {
GraphEntryInstr* graph_entry = flow_graph->graph_entry();
auto normal_entry = graph_entry->normal_entry();
BlockBuilder builder(flow_graph, normal_entry);
Definition* value = builder.AddParameter(1);
Definition* index = builder.AddParameter(2);
Definition* array = builder.AddParameter(3);
PrepareIndexedOp(&builder, array, index, Array::length_offset());
builder.AddInstruction(new StoreIndexedInstr(
new Value(array), new Value(index), new Value(value), kEmitStoreBarrier,
Instance::ElementSizeFor(kArrayCid), // index scale
kArrayCid, kAlignedAccess, DeoptId::kNone, builder.TokenPos()));
// Return null.
Definition* null_def = builder.AddNullDefinition();
builder.AddIntrinsicReturn(new Value(null_def));
return true;
}
bool Intrinsifier::Build_GrowableArraySetIndexed(FlowGraph* flow_graph) {

View file

@ -53,31 +53,6 @@ void Intrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
assembler->mov(LR, Operand(CALLEE_SAVED_TEMP));
}
// Intrinsify only for Smi index.
void Intrinsifier::ObjectArraySetIndexedUnchecked(Assembler* assembler,
Label* normal_ir_body) {
__ ldr(R1, Address(SP, 1 * kWordSize)); // Index.
__ tst(R1, Operand(kSmiTagMask));
// Index not Smi.
__ b(normal_ir_body, NE);
__ ldr(R0, Address(SP, 2 * kWordSize)); // Array.
// Range check.
__ ldr(R3, FieldAddress(R0, Array::length_offset())); // Array length.
__ cmp(R1, Operand(R3));
// Runtime throws exception.
__ b(normal_ir_body, CS);
// Note that R1 is Smi, i.e, times 2.
ASSERT(kSmiTagShift == 1);
__ ldr(R2, Address(SP, 0 * kWordSize)); // Value.
__ add(R1, R0, Operand(R1, LSL, 1)); // R1 is Smi.
__ StoreIntoObject(R0, FieldAddress(R1, Array::data_offset()), R2);
// Caller is responsible for preserving the value if necessary.
__ Ret();
__ Bind(normal_ir_body);
}
// Allocate a GrowableObjectArray using the backing array specified.
// On stack: type argument (+1), data (+0).
void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
@ -112,40 +87,6 @@ void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
__ Bind(normal_ir_body);
}
// Add an element to growable array if it doesn't need to grow, otherwise
// call into regular code.
// On stack: growable array (+1), value (+0).
void Intrinsifier::GrowableArray_add(Assembler* assembler,
Label* normal_ir_body) {
// In checked mode we need to type-check the incoming argument.
if (Isolate::Current()->argument_type_checks()) {
return;
}
// R0: Array.
__ ldr(R0, Address(SP, 1 * kWordSize));
// R1: length.
__ ldr(R1, FieldAddress(R0, GrowableObjectArray::length_offset()));
// R2: data.
__ ldr(R2, FieldAddress(R0, GrowableObjectArray::data_offset()));
// R3: capacity.
__ ldr(R3, FieldAddress(R2, Array::length_offset()));
// Compare length with capacity.
__ cmp(R1, Operand(R3));
__ b(normal_ir_body, EQ); // Must grow data.
const int32_t value_one = reinterpret_cast<int32_t>(Smi::New(1));
// len = len + 1;
__ add(R3, R1, Operand(value_one));
__ StoreIntoSmiField(FieldAddress(R0, GrowableObjectArray::length_offset()),
R3);
__ ldr(R0, Address(SP, 0 * kWordSize)); // Value.
ASSERT(kSmiTagShift == 1);
__ add(R1, R2, Operand(R1, LSL, 1));
__ StoreIntoObject(R2, FieldAddress(R1, Array::data_offset()), R0);
__ LoadObject(R0, Object::null_object());
__ Ret();
__ Bind(normal_ir_body);
}
#define TYPED_ARRAY_ALLOCATION(type_name, cid, max_len, scale_shift) \
Label fall_through; \
const intptr_t kArrayLengthStackOffset = 0 * kWordSize; \

View file

@ -57,29 +57,6 @@ void Intrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
assembler->mov(ARGS_DESC_REG, CALLEE_SAVED_TEMP2);
}
// Intrinsify only for Smi index.
void Intrinsifier::ObjectArraySetIndexedUnchecked(Assembler* assembler,
Label* normal_ir_body) {
__ ldr(R1, Address(SP, 1 * kWordSize)); // Index.
__ BranchIfNotSmi(R1, normal_ir_body);
__ ldr(R0, Address(SP, 2 * kWordSize)); // Array.
// Range check.
__ ldr(R3, FieldAddress(R0, Array::length_offset())); // Array length.
__ cmp(R1, Operand(R3));
// Runtime throws exception.
__ b(normal_ir_body, CS);
// Note that R1 is Smi, i.e, times 2.
ASSERT(kSmiTagShift == 1);
__ ldr(R2, Address(SP, 0 * kWordSize)); // Value.
__ add(R1, R0, Operand(R1, LSL, 2)); // R1 is Smi.
__ StoreIntoObject(R0, FieldAddress(R1, Array::data_offset()), R2);
// Caller is responsible for preserving the value if necessary.
__ ret();
__ Bind(normal_ir_body);
}
// Allocate a GrowableObjectArray using the backing array specified.
// On stack: type argument (+1), data (+0).
void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
@ -113,39 +90,6 @@ void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
__ Bind(normal_ir_body);
}
// Add an element to growable array if it doesn't need to grow, otherwise
// call into regular code.
// On stack: growable array (+1), value (+0).
void Intrinsifier::GrowableArray_add(Assembler* assembler,
Label* normal_ir_body) {
// In checked mode we need to type-check the incoming argument.
if (Isolate::Current()->argument_type_checks()) {
return;
}
// R0: Array.
__ ldr(R0, Address(SP, 1 * kWordSize));
// R1: length.
__ ldr(R1, FieldAddress(R0, GrowableObjectArray::length_offset()));
// R2: data.
__ ldr(R2, FieldAddress(R0, GrowableObjectArray::data_offset()));
// R3: capacity.
__ ldr(R3, FieldAddress(R2, Array::length_offset()));
// Compare length with capacity.
__ cmp(R1, Operand(R3));
__ b(normal_ir_body, EQ); // Must grow data.
const int64_t value_one = reinterpret_cast<int64_t>(Smi::New(1));
// len = len + 1;
__ add(R3, R1, Operand(value_one));
__ str(R3, FieldAddress(R0, GrowableObjectArray::length_offset()));
__ ldr(R0, Address(SP, 0 * kWordSize)); // Value.
ASSERT(kSmiTagShift == 1);
__ add(R1, R2, Operand(R1, LSL, 2));
__ StoreIntoObject(R2, FieldAddress(R1, Array::data_offset()), R0);
__ LoadObject(R0, Object::null_object());
__ ret();
__ Bind(normal_ir_body);
}
static int GetScaleFactor(intptr_t size) {
switch (size) {
case 1:

View file

@ -52,29 +52,6 @@ void Intrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
assembler->movl(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
}
// Intrinsify only for Smi index.
void Intrinsifier::ObjectArraySetIndexedUnchecked(Assembler* assembler,
Label* normal_ir_body) {
__ movl(EBX, Address(ESP, +2 * kWordSize)); // Index.
__ testl(EBX, Immediate(kSmiTagMask));
// Index not Smi.
__ j(NOT_ZERO, normal_ir_body);
__ movl(EAX, Address(ESP, +3 * kWordSize)); // Array.
// Range check.
__ cmpl(EBX, FieldAddress(EAX, Array::length_offset()));
// Runtime throws exception.
__ j(ABOVE_EQUAL, normal_ir_body);
// Note that EBX is Smi, i.e, times 2.
ASSERT(kSmiTagShift == 1);
// Destroy ECX (ic data) as we will not continue in the function.
__ movl(ECX, Address(ESP, +1 * kWordSize)); // Value.
__ StoreIntoObject(EAX, FieldAddress(EAX, EBX, TIMES_2, Array::data_offset()),
ECX);
// Caller is responsible of preserving the value if necessary.
__ ret();
__ Bind(normal_ir_body);
}
// Allocate a GrowableObjectArray using the backing array specified.
// On stack: type argument (+2), data (+1), return-address (+0).
void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
@ -110,35 +87,6 @@ void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
__ Bind(normal_ir_body);
}
// Add an element to growable array if it doesn't need to grow, otherwise
// call into regular code.
// On stack: growable array (+2), value (+1), return-address (+0).
void Intrinsifier::GrowableArray_add(Assembler* assembler,
Label* normal_ir_body) {
// In checked mode we need to type-check the incoming argument.
if (Isolate::Current()->argument_type_checks()) return;
__ movl(EAX, Address(ESP, +2 * kWordSize)); // Array.
__ movl(EBX, FieldAddress(EAX, GrowableObjectArray::length_offset()));
// EBX: length.
__ movl(EDI, FieldAddress(EAX, GrowableObjectArray::data_offset()));
// EDI: data.
// Compare length with capacity.
__ cmpl(EBX, FieldAddress(EDI, Array::length_offset()));
__ j(EQUAL, normal_ir_body); // Must grow data.
__ IncrementSmiField(FieldAddress(EAX, GrowableObjectArray::length_offset()),
1);
__ movl(EAX, Address(ESP, +1 * kWordSize)); // Value
ASSERT(kSmiTagShift == 1);
__ StoreIntoObject(EDI, FieldAddress(EDI, EBX, TIMES_2, Array::data_offset()),
EAX);
const Immediate& raw_null =
Immediate(reinterpret_cast<int32_t>(Object::null()));
__ movl(EAX, raw_null);
__ ret();
__ Bind(normal_ir_body);
}
#define TYPED_ARRAY_ALLOCATION(type_name, cid, max_len, scale_factor) \
const intptr_t kArrayLengthStackOffset = 1 * kWordSize; \
NOT_IN_PRODUCT(__ MaybeTraceAllocation(cid, EDI, normal_ir_body, false)); \

View file

@ -52,27 +52,6 @@ void Intrinsifier::IntrinsicCallEpilogue(Assembler* assembler) {
assembler->movq(ARGS_DESC_REG, CALLEE_SAVED_TEMP);
}
void Intrinsifier::ObjectArraySetIndexedUnchecked(Assembler* assembler,
Label* normal_ir_body) {
__ movq(RDX, Address(RSP, +1 * kWordSize)); // Value.
__ movq(RCX, Address(RSP, +2 * kWordSize)); // Index.
__ movq(RAX, Address(RSP, +3 * kWordSize)); // Array.
__ testq(RCX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, normal_ir_body);
// Range check.
__ cmpq(RCX, FieldAddress(RAX, Array::length_offset()));
// Runtime throws exception.
__ j(ABOVE_EQUAL, normal_ir_body);
// Note that RBX is Smi, i.e, times 2.
ASSERT(kSmiTagShift == 1);
// Destroy RCX (ic data) as we will not continue in the function.
__ StoreIntoObject(RAX, FieldAddress(RAX, RCX, TIMES_4, Array::data_offset()),
RDX);
// Caller is responsible of preserving the value if necessary.
__ ret();
__ Bind(normal_ir_body);
}
// Allocate a GrowableObjectArray using the backing array specified.
// On stack: type argument (+2), data (+1), return-address (+0).
void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
@ -108,34 +87,6 @@ void Intrinsifier::GrowableArray_Allocate(Assembler* assembler,
__ Bind(normal_ir_body);
}
// Add an element to growable array if it doesn't need to grow, otherwise
// call into regular code.
// On stack: growable array (+2), value (+1), return-address (+0).
void Intrinsifier::GrowableArray_add(Assembler* assembler,
Label* normal_ir_body) {
// In checked mode we need to check the incoming argument.
if (Isolate::Current()->argument_type_checks()) return;
__ movq(RAX, Address(RSP, +2 * kWordSize)); // Array.
__ movq(RCX, FieldAddress(RAX, GrowableObjectArray::length_offset()));
// RCX: length.
__ movq(RDX, FieldAddress(RAX, GrowableObjectArray::data_offset()));
// RDX: data.
// Compare length with capacity.
__ cmpq(RCX, FieldAddress(RDX, Array::length_offset()));
__ j(EQUAL, normal_ir_body); // Must grow data.
// len = len + 1;
__ IncrementSmiField(FieldAddress(RAX, GrowableObjectArray::length_offset()),
1);
__ movq(RAX, Address(RSP, +1 * kWordSize)); // Value
ASSERT(kSmiTagShift == 1);
__ StoreIntoObject(RDX, FieldAddress(RDX, RCX, TIMES_4, Array::data_offset()),
RAX);
__ LoadObject(RAX, Object::null_object());
__ ret();
__ Bind(normal_ir_body);
}
#define TYPED_ARRAY_ALLOCATION(type_name, cid, max_len, scale_factor) \
Label fall_through; \
const intptr_t kArrayLengthStackOffset = 1 * kWordSize; \

View file

@ -169,11 +169,8 @@ namespace dart {
V(_Double, get:isNegative, Double_getIsNegative, Bool, 0x3a59e7f4) \
V(_Double, _mulFromInteger, Double_mulFromInteger, Double, 0x2017fcf6) \
V(_Double, .fromInteger, DoubleFromInteger, Double, 0x6d234f4b) \
V(_List, _setIndexed, ObjectArraySetIndexedUnchecked, Dynamic, 0x50d64c75) \
V(_List, []=, ObjectArraySetIndexed, Dynamic, 0x16b3d2b0) \
V(_GrowableList, .withData, GrowableArray_Allocate, GrowableObjectArray, \
0x28b2138e) \
V(_GrowableList, add, GrowableArray_add, Dynamic, 0x40b490b8) \
V(_RegExp, _ExecuteMatch, RegExp_ExecuteMatch, Dynamic, 0x380184b1) \
V(_RegExp, _ExecuteMatchSticky, RegExp_ExecuteMatchSticky, Dynamic, \
0x79b8f955) \
@ -325,6 +322,8 @@ namespace dart {
#define GRAPH_CORE_INTRINSICS_LIST(V) \
V(_List, get:length, ObjectArrayLength, Smi, 0x25952390) \
V(_List, [], ObjectArrayGetIndexed, Dynamic, 0x653da02e) \
V(_List, []=, ObjectArraySetIndexed, Dynamic, 0x16b3d2b0) \
V(_List, _setIndexed, ObjectArraySetIndexedUnchecked, Dynamic, 0x50d64c75) \
V(_ImmutableList, get:length, ImmutableArrayLength, Smi, 0x25952390) \
V(_ImmutableList, [], ImmutableArrayGetIndexed, Dynamic, 0x653da02e) \
V(_GrowableList, get:length, GrowableArrayLength, Smi, 0x18dd86b4) \