Index: src/ic/accessor-assembler.cc |
diff --git a/src/ic/accessor-assembler.cc b/src/ic/accessor-assembler.cc |
index 447258a509c43a97a45b1b929ba2b0b0b030eb74..1cb23279538bb854dd6b0c4d477bef24db39bb51 100644 |
--- a/src/ic/accessor-assembler.cc |
+++ b/src/ic/accessor-assembler.cc |
@@ -1098,6 +1098,222 @@ void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, Node* name, |
Bind(&done); |
} |
+void AccessorAssembler::GenericElementLoad(Node* receiver, Node* receiver_map, |
+ Node* instance_type, Node* index, |
+ Label* slow) { |
+ Comment("integer index"); |
+ Label if_element_hole(this), if_oob(this); |
+ // Receivers requiring non-standard element accesses (interceptors, access |
+ // checks, strings and string wrappers, proxies) are handled in the runtime. |
+ GotoIf(Int32LessThanOrEqual(instance_type, |
+ Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)), |
+ slow); |
+ Node* elements = LoadElements(receiver); |
+ Node* elements_kind = LoadMapElementsKind(receiver_map); |
+ Node* is_jsarray_condition = |
+ Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)); |
+ Variable var_double_value(this, MachineRepresentation::kFloat64); |
+ Label rebox_double(this, &var_double_value); |
+ |
+ // Unimplemented elements kinds fall back to a runtime call. |
+ Label* unimplemented_elements_kind = slow; |
+ IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1); |
+ EmitElementLoad(receiver, elements, elements_kind, index, |
+ is_jsarray_condition, &if_element_hole, &rebox_double, |
+ &var_double_value, unimplemented_elements_kind, &if_oob, |
+ slow); |
+ |
+ Bind(&rebox_double); |
+ Return(AllocateHeapNumberWithValue(var_double_value.value())); |
+ |
+ Bind(&if_oob); |
+ { |
+ Comment("out of bounds"); |
+ // Negative keys can't take the fast OOB path. |
+ GotoIf(IntPtrLessThan(index, IntPtrConstant(0)), slow); |
+ // Positive OOB indices are effectively the same as hole loads. |
+ Goto(&if_element_hole); |
+ } |
+ |
+ Bind(&if_element_hole); |
+ { |
+ Comment("found the hole"); |
+ Label return_undefined(this); |
+ BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, slow); |
+ |
+ Bind(&return_undefined); |
+ Return(UndefinedConstant()); |
+ } |
+} |
+ |
+void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map, |
+ Node* instance_type, Node* key, |
+ const LoadICParameters* p, |
+ Label* slow) { |
+ Comment("key is unique name"); |
+ Label if_found_on_receiver(this), if_property_dictionary(this), |
+ lookup_prototype_chain(this); |
+ Variable var_details(this, MachineRepresentation::kWord32); |
+ Variable var_value(this, MachineRepresentation::kTagged); |
+ |
+ // Receivers requiring non-standard accesses (interceptors, access |
+ // checks, string wrappers, proxies) are handled in the runtime. |
+ // We special-case strings here, to support loading <Symbol.split> etc. |
+ Variable var_receiver(this, MachineRepresentation::kTagged); |
+ Variable var_receiver_map(this, MachineRepresentation::kTagged); |
+ Variable var_instance_type(this, MachineRepresentation::kWord32); |
+ var_receiver.Bind(receiver); |
+ var_receiver_map.Bind(receiver_map); |
+ var_instance_type.Bind(instance_type); |
+ Label normal_receiver(this); |
+ GotoIf(Int32GreaterThan(instance_type, |
+ Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)), |
+ &normal_receiver); |
+ GotoIf(Int32GreaterThanOrEqual(instance_type, |
+ Int32Constant(FIRST_NONSTRING_TYPE)), |
+ slow); |
+ CSA_ASSERT(this, WordEqual(LoadMapConstructorFunctionIndex(receiver_map), |
+ IntPtrConstant(Context::STRING_FUNCTION_INDEX))); |
+ Node* native_context = LoadNativeContext(p->context); |
+ Node* constructor_function = |
+ LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX); |
+ Node* initial_map = LoadObjectField(constructor_function, |
+ JSFunction::kPrototypeOrInitialMapOffset); |
+ var_receiver.Bind(LoadMapPrototype(initial_map)); |
+ var_receiver_map.Bind(LoadMap(var_receiver.value())); |
+ var_instance_type.Bind(LoadMapInstanceType(var_receiver_map.value())); |
+ Goto(&normal_receiver); |
+ |
+ Bind(&normal_receiver); |
+ receiver = var_receiver.value(); |
+ receiver_map = var_receiver_map.value(); |
+ instance_type = var_instance_type.value(); |
+ // Check if the receiver has fast or slow properties. |
+ Node* properties = LoadProperties(receiver); |
+ Node* properties_map = LoadMap(properties); |
+ GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)), |
+ &if_property_dictionary); |
+ |
+ // Try looking up the property on the receiver; if unsuccessful, look |
+ // for a handler in the stub cache. |
+ Comment("DescriptorArray lookup"); |
+ |
+ // Skip linear search if there are too many descriptors. |
+ // TODO(jkummerow): Consider implementing binary search. |
+ // See also TryLookupProperty() which has the same limitation. |
+ const int32_t kMaxLinear = 210; |
+ Label stub_cache(this); |
+ Node* bitfield3 = LoadMapBitField3(var_receiver_map.value()); |
+ Node* nof = DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3); |
+ GotoIf(UintPtrLessThan(IntPtrConstant(kMaxLinear), nof), &stub_cache); |
+ Node* descriptors = LoadMapDescriptors(var_receiver_map.value()); |
+ Variable var_name_index(this, MachineType::PointerRepresentation()); |
+ Label if_descriptor_found(this); |
+ DescriptorLookupLinear(key, descriptors, nof, &if_descriptor_found, |
+ &var_name_index, &stub_cache); |
+ |
+ Bind(&if_descriptor_found); |
+ { |
+ LoadPropertyFromFastObject(receiver, var_receiver_map.value(), descriptors, |
+ var_name_index.value(), &var_details, |
+ &var_value); |
+ Goto(&if_found_on_receiver); |
+ } |
+ |
+ Bind(&stub_cache); |
+ { |
+ Comment("stub cache probe for fast property load"); |
+ Variable var_handler(this, MachineRepresentation::kTagged); |
+ Label found_handler(this, &var_handler), stub_cache_miss(this); |
+ TryProbeStubCache(isolate()->load_stub_cache(), receiver, key, |
+ &found_handler, &var_handler, &stub_cache_miss); |
+ Bind(&found_handler); |
+ { HandleLoadICHandlerCase(p, var_handler.value(), slow); } |
+ |
+ Bind(&stub_cache_miss); |
+ { |
+ // TODO(jkummerow): Check if the property exists on the prototype |
+ // chain. If it doesn't, then there's no point in missing. |
+ Comment("KeyedLoadGeneric_miss"); |
+ TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver, |
+ p->name, p->slot, p->vector); |
+ } |
+ } |
+ |
+ Bind(&if_property_dictionary); |
+ { |
+ Comment("dictionary property load"); |
+ // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out |
+ // seeing global objects here (which would need special handling). |
+ |
+ Variable var_name_index(this, MachineType::PointerRepresentation()); |
+ Label dictionary_found(this, &var_name_index); |
+ NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found, |
+ &var_name_index, |
+ &lookup_prototype_chain); |
+ Bind(&dictionary_found); |
+ { |
+ LoadPropertyFromNameDictionary(properties, var_name_index.value(), |
+ &var_details, &var_value); |
+ Goto(&if_found_on_receiver); |
+ } |
+ } |
+ |
+ Bind(&if_found_on_receiver); |
+ { |
+ Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(), |
+ p->context, var_receiver.value(), slow); |
+ IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1); |
+ Return(value); |
+ } |
+ |
+ Bind(&lookup_prototype_chain); |
+ { |
+ Variable var_holder_map(this, MachineRepresentation::kTagged); |
+ Variable var_holder_instance_type(this, MachineRepresentation::kWord32); |
+ Label return_undefined(this); |
+ Variable* merged_variables[] = {&var_holder_map, &var_holder_instance_type}; |
+ Label loop(this, arraysize(merged_variables), merged_variables); |
+ |
+ var_holder_map.Bind(var_receiver_map.value()); |
+ var_holder_instance_type.Bind(var_instance_type.value()); |
+ // Private symbols must not be looked up on the prototype chain. |
+ GotoIf(IsPrivateSymbol(key), &return_undefined); |
+ Goto(&loop); |
+ Bind(&loop); |
+ { |
+ // Bailout if it can be an integer indexed exotic case. |
+ GotoIf(Word32Equal(var_holder_instance_type.value(), |
+ Int32Constant(JS_TYPED_ARRAY_TYPE)), |
+ slow); |
+ Node* proto = LoadMapPrototype(var_holder_map.value()); |
+ GotoIf(WordEqual(proto, NullConstant()), &return_undefined); |
+ Node* proto_map = LoadMap(proto); |
+ Node* proto_instance_type = LoadMapInstanceType(proto_map); |
+ var_holder_map.Bind(proto_map); |
+ var_holder_instance_type.Bind(proto_instance_type); |
+ Label next_proto(this), return_value(this, &var_value), goto_slow(this); |
+ TryGetOwnProperty(p->context, var_receiver.value(), proto, proto_map, |
+ proto_instance_type, key, &return_value, &var_value, |
+ &next_proto, &goto_slow); |
+ |
+ // This trampoline and the next are required to appease Turbofan's |
+ // variable merging. |
+ Bind(&next_proto); |
+ Goto(&loop); |
+ |
+ Bind(&goto_slow); |
+ Goto(slow); |
+ |
+ Bind(&return_value); |
+ Return(var_value.value()); |
+ } |
+ |
+ Bind(&return_undefined); |
+ Return(UndefinedConstant()); |
+ } |
+} |
+ |
//////////////////// Stub cache access helpers. |
enum AccessorAssembler::StubCacheTable : int { |
@@ -1399,198 +1615,26 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) { |
Variable var_index(this, MachineType::PointerRepresentation()); |
Variable var_unique(this, MachineRepresentation::kTagged); |
var_unique.Bind(p->name); // Dummy initialization. |
- Variable var_details(this, MachineRepresentation::kWord32); |
- Variable var_value(this, MachineRepresentation::kTagged); |
- Label if_index(this), if_unique_name(this), if_element_hole(this), |
- if_oob(this), slow(this), stub_cache_miss(this), |
- if_property_dictionary(this), if_found_on_receiver(this), |
- lookup_prototype_chain(this); |
+ Label if_index(this), if_unique_name(this), slow(this); |
Node* receiver = p->receiver; |
GotoIf(TaggedIsSmi(receiver), &slow); |
Node* receiver_map = LoadMap(receiver); |
Node* instance_type = LoadMapInstanceType(receiver_map); |
- // Receivers requiring non-standard element accesses (interceptors, access |
- // checks, strings and string wrappers, proxies) are handled in the runtime. |
- GotoIf(Int32LessThanOrEqual(instance_type, |
- Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)), |
- &slow); |
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, |
&slow); |
Bind(&if_index); |
{ |
- Comment("integer index"); |
- Node* index = var_index.value(); |
- Node* elements = LoadElements(receiver); |
- Node* elements_kind = LoadMapElementsKind(receiver_map); |
- Node* is_jsarray_condition = |
- Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)); |
- Variable var_double_value(this, MachineRepresentation::kFloat64); |
- Label rebox_double(this, &var_double_value); |
- |
- // Unimplemented elements kinds fall back to a runtime call. |
- Label* unimplemented_elements_kind = &slow; |
- IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1); |
- EmitElementLoad(receiver, elements, elements_kind, index, |
- is_jsarray_condition, &if_element_hole, &rebox_double, |
- &var_double_value, unimplemented_elements_kind, &if_oob, |
- &slow); |
- |
- Bind(&rebox_double); |
- Return(AllocateHeapNumberWithValue(var_double_value.value())); |
+ GenericElementLoad(receiver, receiver_map, instance_type, var_index.value(), |
+ &slow); |
} |
- Bind(&if_oob); |
- { |
- Comment("out of bounds"); |
- Node* index = var_index.value(); |
- // Negative keys can't take the fast OOB path. |
- GotoIf(IntPtrLessThan(index, IntPtrConstant(0)), &slow); |
- // Positive OOB indices are effectively the same as hole loads. |
- Goto(&if_element_hole); |
- } |
- |
- Bind(&if_element_hole); |
- { |
- Comment("found the hole"); |
- Label return_undefined(this); |
- BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, &slow); |
- |
- Bind(&return_undefined); |
- Return(UndefinedConstant()); |
- } |
- |
- Node* properties = nullptr; |
Bind(&if_unique_name); |
{ |
- Comment("key is unique name"); |
- Node* key = var_unique.value(); |
- // Check if the receiver has fast or slow properties. |
- properties = LoadProperties(receiver); |
- Node* properties_map = LoadMap(properties); |
- GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)), |
- &if_property_dictionary); |
- |
- // Try looking up the property on the receiver; if unsuccessful, look |
- // for a handler in the stub cache. |
- Comment("DescriptorArray lookup"); |
- |
- // Skip linear search if there are too many descriptors. |
- // TODO(jkummerow): Consider implementing binary search. |
- // See also TryLookupProperty() which has the same limitation. |
- const int32_t kMaxLinear = 210; |
- Label stub_cache(this); |
- Node* bitfield3 = LoadMapBitField3(receiver_map); |
- Node* nof = |
- DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3); |
- GotoIf(UintPtrLessThan(IntPtrConstant(kMaxLinear), nof), &stub_cache); |
- Node* descriptors = LoadMapDescriptors(receiver_map); |
- Variable var_name_index(this, MachineType::PointerRepresentation()); |
- Label if_descriptor_found(this); |
- DescriptorLookupLinear(key, descriptors, nof, &if_descriptor_found, |
- &var_name_index, &stub_cache); |
- |
- Bind(&if_descriptor_found); |
- { |
- LoadPropertyFromFastObject(receiver, receiver_map, descriptors, |
- var_name_index.value(), &var_details, |
- &var_value); |
- Goto(&if_found_on_receiver); |
- } |
- |
- Bind(&stub_cache); |
- { |
- Comment("stub cache probe for fast property load"); |
- Variable var_handler(this, MachineRepresentation::kTagged); |
- Label found_handler(this, &var_handler), stub_cache_miss(this); |
- TryProbeStubCache(isolate()->load_stub_cache(), receiver, key, |
- &found_handler, &var_handler, &stub_cache_miss); |
- Bind(&found_handler); |
- { HandleLoadICHandlerCase(p, var_handler.value(), &slow); } |
- |
- Bind(&stub_cache_miss); |
- { |
- Comment("KeyedLoadGeneric_miss"); |
- TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver, |
- p->name, p->slot, p->vector); |
- } |
- } |
- } |
- |
- Bind(&if_property_dictionary); |
- { |
- Comment("dictionary property load"); |
- // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out |
- // seeing global objects here (which would need special handling). |
- |
- Node* key = var_unique.value(); |
- Variable var_name_index(this, MachineType::PointerRepresentation()); |
- Label dictionary_found(this, &var_name_index); |
- NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found, |
- &var_name_index, |
- &lookup_prototype_chain); |
- Bind(&dictionary_found); |
- { |
- LoadPropertyFromNameDictionary(properties, var_name_index.value(), |
- &var_details, &var_value); |
- Goto(&if_found_on_receiver); |
- } |
- } |
- |
- Bind(&if_found_on_receiver); |
- { |
- Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(), |
- p->context, receiver, &slow); |
- IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1); |
- Return(value); |
- } |
- |
- Bind(&lookup_prototype_chain); |
- { |
- Variable var_holder_map(this, MachineRepresentation::kTagged); |
- Variable var_holder_instance_type(this, MachineRepresentation::kWord32); |
- Label return_undefined(this); |
- Variable* merged_variables[] = {&var_holder_map, &var_holder_instance_type}; |
- Label loop(this, arraysize(merged_variables), merged_variables); |
- |
- var_holder_map.Bind(receiver_map); |
- var_holder_instance_type.Bind(instance_type); |
- // Private symbols must not be looked up on the prototype chain. |
- GotoIf(IsPrivateSymbol(var_unique.value()), &return_undefined); |
- Goto(&loop); |
- Bind(&loop); |
- { |
- // Bailout if it can be an integer indexed exotic case. |
- GotoIf(Word32Equal(var_holder_instance_type.value(), |
- Int32Constant(JS_TYPED_ARRAY_TYPE)), |
- &slow); |
- Node* proto = LoadMapPrototype(var_holder_map.value()); |
- GotoIf(WordEqual(proto, NullConstant()), &return_undefined); |
- Node* proto_map = LoadMap(proto); |
- Node* proto_instance_type = LoadMapInstanceType(proto_map); |
- var_holder_map.Bind(proto_map); |
- var_holder_instance_type.Bind(proto_instance_type); |
- Label next_proto(this), return_value(this, &var_value), goto_slow(this); |
- TryGetOwnProperty(p->context, receiver, proto, proto_map, |
- proto_instance_type, var_unique.value(), &return_value, |
- &var_value, &next_proto, &goto_slow); |
- |
- // This trampoline and the next are required to appease Turbofan's |
- // variable merging. |
- Bind(&next_proto); |
- Goto(&loop); |
- |
- Bind(&goto_slow); |
- Goto(&slow); |
- |
- Bind(&return_value); |
- Return(var_value.value()); |
- } |
- |
- Bind(&return_undefined); |
- Return(UndefinedConstant()); |
+ GenericPropertyLoad(receiver, receiver_map, instance_type, |
+ var_unique.value(), p, &slow); |
} |
Bind(&slow); |