[stubs] Port KeyedStoreIC_Megamorphic stub to Turbofan
BUG=v8:5269,v8:5561 Review-Url: https://codereview.chromium.org/2444353002 Cr-Commit-Position: refs/heads/master@{#40896}
This commit is contained in:
parent
cc2a2771a6
commit
5c03cb7922
2
BUILD.gn
2
BUILD.gn
@ -1418,6 +1418,8 @@ v8_source_set("v8_base") {
|
||||
"src/ic/ic-state.h",
|
||||
"src/ic/ic.cc",
|
||||
"src/ic/ic.h",
|
||||
"src/ic/keyed-store-generic.cc",
|
||||
"src/ic/keyed-store-generic.h",
|
||||
"src/ic/stub-cache.cc",
|
||||
"src/ic/stub-cache.h",
|
||||
"src/icu_util.cc",
|
||||
|
@ -2,10 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/ic/handler-compiler.h"
|
||||
#include "src/ic/ic.h"
|
||||
#include "src/ic/keyed-store-generic.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -40,6 +41,32 @@ void Builtins::Generate_KeyedStoreIC_Megamorphic_Strict(MacroAssembler* masm) {
|
||||
KeyedStoreIC::GenerateMegamorphic(masm, STRICT);
|
||||
}
|
||||
|
||||
void KeyedStoreICMegamorphic(CodeStubAssembler* assembler, LanguageMode mode) {
|
||||
typedef compiler::Node Node;
|
||||
typedef StoreWithVectorDescriptor Descriptor;
|
||||
|
||||
Node* receiver = assembler->Parameter(Descriptor::kReceiver);
|
||||
Node* name = assembler->Parameter(Descriptor::kName);
|
||||
Node* value = assembler->Parameter(Descriptor::kValue);
|
||||
Node* slot = assembler->Parameter(Descriptor::kSlot);
|
||||
Node* vector = assembler->Parameter(Descriptor::kVector);
|
||||
Node* context = assembler->Parameter(Descriptor::kContext);
|
||||
|
||||
CodeStubAssembler::StoreICParameters p(context, receiver, name, value, slot,
|
||||
vector);
|
||||
KeyedStoreGenericGenerator::Generate(assembler, &p, mode);
|
||||
}
|
||||
|
||||
void Builtins::Generate_KeyedStoreIC_Megamorphic_TF(
|
||||
CodeStubAssembler* assembler) {
|
||||
KeyedStoreICMegamorphic(assembler, SLOPPY);
|
||||
}
|
||||
|
||||
void Builtins::Generate_KeyedStoreIC_Megamorphic_Strict_TF(
|
||||
CodeStubAssembler* assembler) {
|
||||
KeyedStoreICMegamorphic(assembler, STRICT);
|
||||
}
|
||||
|
||||
void Builtins::Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
|
||||
KeyedStoreIC::GenerateMiss(masm);
|
||||
}
|
||||
|
@ -186,6 +186,10 @@ namespace internal {
|
||||
ASH(KeyedStoreIC_Megamorphic, KEYED_STORE_IC, kNoExtraICState) \
|
||||
ASH(KeyedStoreIC_Megamorphic_Strict, KEYED_STORE_IC, \
|
||||
StoreICState::kStrictModeState) \
|
||||
TFS(KeyedStoreIC_Megamorphic_TF, KEYED_STORE_IC, kNoExtraICState, \
|
||||
StoreWithVector) \
|
||||
TFS(KeyedStoreIC_Megamorphic_Strict_TF, KEYED_STORE_IC, \
|
||||
StoreICState::kStrictModeState, StoreWithVector) \
|
||||
ASM(KeyedStoreIC_Miss) \
|
||||
ASH(KeyedStoreIC_Slow, HANDLER, Code::KEYED_STORE_IC) \
|
||||
TFS(LoadGlobalIC_Miss, BUILTIN, kNoExtraICState, LoadGlobalWithVector) \
|
||||
|
@ -123,6 +123,13 @@ Callable CodeFactory::KeyedStoreICInOptimizedCode(Isolate* isolate,
|
||||
// static
|
||||
Callable CodeFactory::KeyedStoreIC_Megamorphic(Isolate* isolate,
|
||||
LanguageMode language_mode) {
|
||||
if (FLAG_tf_store_ic_stub) {
|
||||
return Callable(
|
||||
language_mode == STRICT
|
||||
? isolate->builtins()->KeyedStoreIC_Megamorphic_Strict_TF()
|
||||
: isolate->builtins()->KeyedStoreIC_Megamorphic_TF(),
|
||||
StoreWithVectorDescriptor(isolate));
|
||||
}
|
||||
return Callable(language_mode == STRICT
|
||||
? isolate->builtins()->KeyedStoreIC_Megamorphic_Strict()
|
||||
: isolate->builtins()->KeyedStoreIC_Megamorphic(),
|
||||
|
@ -2245,10 +2245,6 @@ Node* CodeStubAssembler::GrowElementsCapacity(
|
||||
// Allocate the new backing store.
|
||||
Node* new_elements = AllocateFixedArray(to_kind, new_capacity, mode);
|
||||
|
||||
// Fill in the added capacity in the new store with holes.
|
||||
FillFixedArrayWithValue(to_kind, new_elements, capacity, new_capacity,
|
||||
Heap::kTheHoleValueRootIndex, mode);
|
||||
|
||||
// Copy the elements from the old elements store to the new.
|
||||
// The size-check above guarantees that the |new_elements| is allocated
|
||||
// in new space so we can skip the write barrier.
|
||||
@ -5278,6 +5274,7 @@ void CodeStubAssembler::EmitFastElementsBoundsCheck(Node* object,
|
||||
Node* is_jsarray_condition,
|
||||
Label* miss) {
|
||||
Variable var_length(this, MachineType::PointerRepresentation());
|
||||
Comment("Fast elements bounds check");
|
||||
Label if_array(this), length_loaded(this, &var_length);
|
||||
GotoIf(is_jsarray_condition, &if_array);
|
||||
{
|
||||
@ -5302,7 +5299,7 @@ void CodeStubAssembler::EmitElementLoad(Node* object, Node* elements,
|
||||
Label* out_of_bounds, Label* miss) {
|
||||
Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this),
|
||||
if_fast_double(this), if_fast_holey_double(this), if_nonfast(this),
|
||||
if_dictionary(this), unreachable(this);
|
||||
if_dictionary(this);
|
||||
GotoIf(
|
||||
IntPtrGreaterThan(elements_kind, IntPtrConstant(LAST_FAST_ELEMENTS_KIND)),
|
||||
&if_nonfast);
|
||||
|
@ -1118,6 +1118,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
ElementsKind kind, ParameterMode mode,
|
||||
int base_size = 0);
|
||||
|
||||
protected:
|
||||
void HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
compiler::Node* handler, Label* miss);
|
||||
|
||||
private:
|
||||
friend class CodeStubArguments;
|
||||
|
||||
@ -1159,9 +1163,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* value,
|
||||
bool transition_to_field, Label* miss);
|
||||
|
||||
void HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
compiler::Node* handler, Label* miss);
|
||||
|
||||
compiler::Node* TryToIntptr(compiler::Node* key, Label* miss);
|
||||
void EmitFastElementsBoundsCheck(compiler::Node* object,
|
||||
compiler::Node* elements,
|
||||
|
@ -833,6 +833,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> handler) {
|
||||
|
||||
Handle<Code> KeyedStoreIC::ChooseMegamorphicStub(Isolate* isolate,
|
||||
ExtraICState extra_state) {
|
||||
DCHECK(!FLAG_tf_store_ic_stub);
|
||||
LanguageMode mode = StoreICState::GetLanguageMode(extra_state);
|
||||
return is_strict(mode)
|
||||
? isolate->builtins()->KeyedStoreIC_Megamorphic_Strict()
|
||||
@ -1713,7 +1714,9 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
|
||||
if ((object->IsJSObject() && key->IsSmi()) ||
|
||||
(object->IsString() && key->IsNumber())) {
|
||||
UpdateLoadElement(Handle<HeapObject>::cast(object));
|
||||
TRACE_IC("LoadIC", key);
|
||||
if (is_vector_set()) {
|
||||
TRACE_IC("LoadIC", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
549
src/ic/keyed-store-generic.cc
Normal file
549
src/ic/keyed-store-generic.cc
Normal file
@ -0,0 +1,549 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/ic/keyed-store-generic.h"
|
||||
|
||||
#include "src/compiler/code-assembler.h"
|
||||
#include "src/contexts.h"
|
||||
#include "src/isolate.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
using compiler::Node;
|
||||
|
||||
class KeyedStoreGenericAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
void KeyedStoreGeneric(const StoreICParameters* p,
|
||||
LanguageMode language_mode);
|
||||
|
||||
private:
|
||||
enum UpdateLength {
|
||||
kDontChangeLength,
|
||||
kIncrementLengthByOne,
|
||||
kBumpLengthWithGap
|
||||
};
|
||||
|
||||
void EmitGenericElementStore(Node* receiver, Node* receiver_map,
|
||||
Node* instance_type, Node* intptr_index,
|
||||
Node* value, Node* context, Label* slow);
|
||||
|
||||
void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
|
||||
const StoreICParameters* p, Label* slow);
|
||||
|
||||
void BranchIfPrototypesHaveNonFastElements(Node* receiver_map,
|
||||
Label* non_fast_elements,
|
||||
Label* only_fast_elements);
|
||||
|
||||
void TryRewriteElements(Node* receiver, Node* receiver_map, Node* elements,
|
||||
Node* native_context, ElementsKind from_kind,
|
||||
ElementsKind to_kind, Label* bailout);
|
||||
|
||||
void StoreElementWithCapacity(Node* receiver, Node* receiver_map,
|
||||
Node* elements, Node* elements_kind,
|
||||
Node* intptr_index, Node* value, Node* context,
|
||||
Label* slow, UpdateLength update_length);
|
||||
|
||||
void MaybeUpdateLengthAndReturn(Node* receiver, Node* index, Node* value,
|
||||
UpdateLength update_length);
|
||||
|
||||
void TryChangeToHoleyMapHelper(Node* receiver, Node* receiver_map,
|
||||
Node* native_context, ElementsKind packed_kind,
|
||||
ElementsKind holey_kind, Label* done,
|
||||
Label* map_mismatch, Label* bailout);
|
||||
void TryChangeToHoleyMap(Node* receiver, Node* receiver_map,
|
||||
Node* current_elements_kind, Node* context,
|
||||
ElementsKind packed_kind, Label* bailout);
|
||||
void TryChangeToHoleyMapMulti(Node* receiver, Node* receiver_map,
|
||||
Node* current_elements_kind, Node* context,
|
||||
ElementsKind packed_kind,
|
||||
ElementsKind packed_kind_2, Label* bailout);
|
||||
|
||||
// Do not add fields, so that this is safe to reinterpret_cast to CSA.
|
||||
};
|
||||
|
||||
void KeyedStoreGenericGenerator::Generate(
|
||||
CodeStubAssembler* assembler, const CodeStubAssembler::StoreICParameters* p,
|
||||
LanguageMode language_mode) {
|
||||
STATIC_ASSERT(sizeof(CodeStubAssembler) ==
|
||||
sizeof(KeyedStoreGenericAssembler));
|
||||
auto assm = reinterpret_cast<KeyedStoreGenericAssembler*>(assembler);
|
||||
assm->KeyedStoreGeneric(p, language_mode);
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::BranchIfPrototypesHaveNonFastElements(
|
||||
Node* receiver_map, Label* non_fast_elements, Label* only_fast_elements) {
|
||||
Variable var_map(this, MachineRepresentation::kTagged);
|
||||
var_map.Bind(receiver_map);
|
||||
Label loop_body(this, &var_map);
|
||||
Goto(&loop_body);
|
||||
|
||||
Bind(&loop_body);
|
||||
{
|
||||
Node* map = var_map.value();
|
||||
Node* prototype = LoadMapPrototype(map);
|
||||
GotoIf(WordEqual(prototype, NullConstant()), only_fast_elements);
|
||||
Node* prototype_map = LoadMap(prototype);
|
||||
var_map.Bind(prototype_map);
|
||||
Node* instance_type = LoadMapInstanceType(prototype_map);
|
||||
STATIC_ASSERT(JS_PROXY_TYPE < JS_OBJECT_TYPE);
|
||||
STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
|
||||
GotoIf(Int32LessThanOrEqual(instance_type,
|
||||
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
|
||||
non_fast_elements);
|
||||
Node* elements_kind = LoadMapElementsKind(prototype_map);
|
||||
STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND);
|
||||
GotoIf(Int32LessThanOrEqual(elements_kind,
|
||||
Int32Constant(LAST_FAST_ELEMENTS_KIND)),
|
||||
&loop_body);
|
||||
GotoIf(Word32Equal(elements_kind, Int32Constant(NO_ELEMENTS)), &loop_body);
|
||||
Goto(non_fast_elements);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::TryRewriteElements(
|
||||
Node* receiver, Node* receiver_map, Node* elements, Node* native_context,
|
||||
ElementsKind from_kind, ElementsKind to_kind, Label* bailout) {
|
||||
DCHECK(IsFastPackedElementsKind(from_kind));
|
||||
ElementsKind holey_from_kind = GetHoleyElementsKind(from_kind);
|
||||
ElementsKind holey_to_kind = GetHoleyElementsKind(to_kind);
|
||||
if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) {
|
||||
TrapAllocationMemento(receiver, bailout);
|
||||
}
|
||||
Label perform_transition(this), check_holey_map(this);
|
||||
Variable var_target_map(this, MachineType::PointerRepresentation());
|
||||
// Check if the receiver has the default |from_kind| map.
|
||||
{
|
||||
Node* packed_map =
|
||||
LoadContextElement(native_context, Context::ArrayMapIndex(from_kind));
|
||||
GotoIf(WordNotEqual(receiver_map, packed_map), &check_holey_map);
|
||||
var_target_map.Bind(
|
||||
LoadContextElement(native_context, Context::ArrayMapIndex(to_kind)));
|
||||
Goto(&perform_transition);
|
||||
}
|
||||
|
||||
// Check if the receiver has the default |holey_from_kind| map.
|
||||
Bind(&check_holey_map);
|
||||
{
|
||||
Node* holey_map = LoadContextElement(
|
||||
native_context, Context::ArrayMapIndex(holey_from_kind));
|
||||
GotoIf(WordNotEqual(receiver_map, holey_map), bailout);
|
||||
var_target_map.Bind(LoadContextElement(
|
||||
native_context, Context::ArrayMapIndex(holey_to_kind)));
|
||||
Goto(&perform_transition);
|
||||
}
|
||||
|
||||
// Found a supported transition target map, perform the transition!
|
||||
Bind(&perform_transition);
|
||||
{
|
||||
if (IsFastDoubleElementsKind(from_kind) !=
|
||||
IsFastDoubleElementsKind(to_kind)) {
|
||||
Node* capacity = LoadFixedArrayBaseLength(elements);
|
||||
GrowElementsCapacity(receiver, elements, from_kind, to_kind, capacity,
|
||||
capacity, INTPTR_PARAMETERS, bailout);
|
||||
}
|
||||
StoreObjectField(receiver, JSObject::kMapOffset, var_target_map.value());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::TryChangeToHoleyMapHelper(
|
||||
Node* receiver, Node* receiver_map, Node* native_context,
|
||||
ElementsKind packed_kind, ElementsKind holey_kind, Label* done,
|
||||
Label* map_mismatch, Label* bailout) {
|
||||
Node* packed_map =
|
||||
LoadContextElement(native_context, Context::ArrayMapIndex(packed_kind));
|
||||
GotoIf(WordNotEqual(receiver_map, packed_map), map_mismatch);
|
||||
if (AllocationSite::GetMode(packed_kind, holey_kind) ==
|
||||
TRACK_ALLOCATION_SITE) {
|
||||
TrapAllocationMemento(receiver, bailout);
|
||||
}
|
||||
Node* holey_map =
|
||||
LoadContextElement(native_context, Context::ArrayMapIndex(holey_kind));
|
||||
StoreObjectField(receiver, JSObject::kMapOffset, holey_map);
|
||||
Goto(done);
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::TryChangeToHoleyMap(
|
||||
Node* receiver, Node* receiver_map, Node* current_elements_kind,
|
||||
Node* context, ElementsKind packed_kind, Label* bailout) {
|
||||
ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
|
||||
Label already_holey(this);
|
||||
|
||||
GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
|
||||
&already_holey);
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
|
||||
holey_kind, &already_holey, bailout, bailout);
|
||||
Bind(&already_holey);
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::TryChangeToHoleyMapMulti(
|
||||
Node* receiver, Node* receiver_map, Node* current_elements_kind,
|
||||
Node* context, ElementsKind packed_kind, ElementsKind packed_kind_2,
|
||||
Label* bailout) {
|
||||
ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
|
||||
ElementsKind holey_kind_2 = GetHoleyElementsKind(packed_kind_2);
|
||||
Label already_holey(this), check_other_kind(this);
|
||||
|
||||
GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
|
||||
&already_holey);
|
||||
GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind_2)),
|
||||
&already_holey);
|
||||
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
|
||||
holey_kind, &already_holey, &check_other_kind,
|
||||
bailout);
|
||||
Bind(&check_other_kind);
|
||||
TryChangeToHoleyMapHelper(receiver, receiver_map, native_context,
|
||||
packed_kind_2, holey_kind_2, &already_holey,
|
||||
bailout, bailout);
|
||||
Bind(&already_holey);
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::MaybeUpdateLengthAndReturn(
|
||||
Node* receiver, Node* index, Node* value, UpdateLength update_length) {
|
||||
if (update_length != kDontChangeLength) {
|
||||
Node* new_length = SmiTag(IntPtrAdd(index, IntPtrConstant(1)));
|
||||
StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset, new_length,
|
||||
MachineRepresentation::kTagged);
|
||||
}
|
||||
Return(value);
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::StoreElementWithCapacity(
|
||||
Node* receiver, Node* receiver_map, Node* elements, Node* elements_kind,
|
||||
Node* intptr_index, Node* value, Node* context, Label* slow,
|
||||
UpdateLength update_length) {
|
||||
if (update_length != kDontChangeLength) {
|
||||
CSA_ASSERT(this, Word32Equal(LoadMapInstanceType(receiver_map),
|
||||
Int32Constant(JS_ARRAY_TYPE)));
|
||||
}
|
||||
STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize);
|
||||
const int kHeaderSize = FixedArray::kHeaderSize - kHeapObjectTag;
|
||||
|
||||
Label check_double_elements(this), check_cow_elements(this);
|
||||
Node* elements_map = LoadMap(elements);
|
||||
GotoIf(WordNotEqual(elements_map, LoadRoot(Heap::kFixedArrayMapRootIndex)),
|
||||
&check_double_elements);
|
||||
|
||||
// FixedArray backing store -> Smi or object elements.
|
||||
{
|
||||
Node* offset = ElementOffsetFromIndex(intptr_index, FAST_ELEMENTS,
|
||||
INTPTR_PARAMETERS, kHeaderSize);
|
||||
// Check if we're about to overwrite the hole. We can safely do that
|
||||
// only if there can be no setters on the prototype chain.
|
||||
// If we know that we're storing beyond the previous array length, we
|
||||
// can skip the hole check (and always assume the hole).
|
||||
{
|
||||
Label hole_check_passed(this);
|
||||
if (update_length == kDontChangeLength) {
|
||||
Node* element = Load(MachineType::AnyTagged(), elements, offset);
|
||||
GotoIf(WordNotEqual(element, TheHoleConstant()), &hole_check_passed);
|
||||
}
|
||||
BranchIfPrototypesHaveNonFastElements(receiver_map, slow,
|
||||
&hole_check_passed);
|
||||
Bind(&hole_check_passed);
|
||||
}
|
||||
|
||||
// Check if the value we're storing matches the elements_kind. Smis
|
||||
// can always be stored.
|
||||
{
|
||||
Label non_smi_value(this);
|
||||
GotoUnless(TaggedIsSmi(value), &non_smi_value);
|
||||
// If we're about to introduce holes, ensure holey elements.
|
||||
if (update_length == kBumpLengthWithGap) {
|
||||
TryChangeToHoleyMapMulti(receiver, receiver_map, elements_kind, context,
|
||||
FAST_SMI_ELEMENTS, FAST_ELEMENTS, slow);
|
||||
}
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, elements, offset,
|
||||
value);
|
||||
MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
|
||||
|
||||
Bind(&non_smi_value);
|
||||
}
|
||||
|
||||
// Check if we already have object elements; just do the store if so.
|
||||
{
|
||||
Label must_transition(this);
|
||||
STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
|
||||
STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
|
||||
GotoIf(Int32LessThanOrEqual(elements_kind,
|
||||
Int32Constant(FAST_HOLEY_SMI_ELEMENTS)),
|
||||
&must_transition);
|
||||
if (update_length == kBumpLengthWithGap) {
|
||||
TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
|
||||
FAST_ELEMENTS, slow);
|
||||
}
|
||||
Store(MachineRepresentation::kTagged, elements, offset, value);
|
||||
MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
|
||||
|
||||
Bind(&must_transition);
|
||||
}
|
||||
|
||||
// Transition to the required ElementsKind.
|
||||
{
|
||||
Label transition_to_double(this), transition_to_object(this);
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Branch(WordEqual(LoadMap(value), LoadRoot(Heap::kHeapNumberMapRootIndex)),
|
||||
&transition_to_double, &transition_to_object);
|
||||
Bind(&transition_to_double);
|
||||
{
|
||||
// If we're adding holes at the end, always transition to a holey
|
||||
// elements kind, otherwise try to remain packed.
|
||||
ElementsKind target_kind = update_length == kBumpLengthWithGap
|
||||
? FAST_HOLEY_DOUBLE_ELEMENTS
|
||||
: FAST_DOUBLE_ELEMENTS;
|
||||
TryRewriteElements(receiver, receiver_map, elements, native_context,
|
||||
FAST_SMI_ELEMENTS, target_kind, slow);
|
||||
// Reload migrated elements.
|
||||
Node* double_elements = LoadElements(receiver);
|
||||
Node* double_offset = ElementOffsetFromIndex(
|
||||
intptr_index, FAST_DOUBLE_ELEMENTS, INTPTR_PARAMETERS, kHeaderSize);
|
||||
// Make sure we do not store signalling NaNs into double arrays.
|
||||
Node* double_value = Float64SilenceNaN(LoadHeapNumberValue(value));
|
||||
StoreNoWriteBarrier(MachineRepresentation::kFloat64, double_elements,
|
||||
double_offset, double_value);
|
||||
MaybeUpdateLengthAndReturn(receiver, intptr_index, value,
|
||||
update_length);
|
||||
}
|
||||
|
||||
Bind(&transition_to_object);
|
||||
{
|
||||
// If we're adding holes at the end, always transition to a holey
|
||||
// elements kind, otherwise try to remain packed.
|
||||
ElementsKind target_kind = update_length == kBumpLengthWithGap
|
||||
? FAST_HOLEY_ELEMENTS
|
||||
: FAST_ELEMENTS;
|
||||
TryRewriteElements(receiver, receiver_map, elements, native_context,
|
||||
FAST_SMI_ELEMENTS, target_kind, slow);
|
||||
// The elements backing store didn't change, no reload necessary.
|
||||
CSA_ASSERT(this, WordEqual(elements, LoadElements(receiver)));
|
||||
Store(MachineRepresentation::kTagged, elements, offset, value);
|
||||
MaybeUpdateLengthAndReturn(receiver, intptr_index, value,
|
||||
update_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&check_double_elements);
|
||||
Node* fixed_double_array_map = LoadRoot(Heap::kFixedDoubleArrayMapRootIndex);
|
||||
GotoIf(WordNotEqual(elements_map, fixed_double_array_map),
|
||||
&check_cow_elements);
|
||||
// FixedDoubleArray backing store -> double elements.
|
||||
{
|
||||
Node* offset = ElementOffsetFromIndex(intptr_index, FAST_DOUBLE_ELEMENTS,
|
||||
INTPTR_PARAMETERS, kHeaderSize);
|
||||
// Check if we're about to overwrite the hole. We can safely do that
|
||||
// only if there can be no setters on the prototype chain.
|
||||
{
|
||||
Label hole_check_passed(this);
|
||||
// If we know that we're storing beyond the previous array length, we
|
||||
// can skip the hole check (and always assume the hole).
|
||||
if (update_length == kDontChangeLength) {
|
||||
Label found_hole(this);
|
||||
LoadDoubleWithHoleCheck(elements, offset, &found_hole,
|
||||
MachineType::None());
|
||||
Goto(&hole_check_passed);
|
||||
Bind(&found_hole);
|
||||
}
|
||||
BranchIfPrototypesHaveNonFastElements(receiver_map, slow,
|
||||
&hole_check_passed);
|
||||
Bind(&hole_check_passed);
|
||||
}
|
||||
|
||||
// Try to store the value as a double.
|
||||
{
|
||||
Label non_number_value(this);
|
||||
Node* double_value = PrepareValueForWrite(value, Representation::Double(),
|
||||
&non_number_value);
|
||||
// Make sure we do not store signalling NaNs into double arrays.
|
||||
double_value = Float64SilenceNaN(double_value);
|
||||
// If we're about to introduce holes, ensure holey elements.
|
||||
if (update_length == kBumpLengthWithGap) {
|
||||
TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
|
||||
FAST_DOUBLE_ELEMENTS, slow);
|
||||
}
|
||||
StoreNoWriteBarrier(MachineRepresentation::kFloat64, elements, offset,
|
||||
double_value);
|
||||
MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
|
||||
|
||||
Bind(&non_number_value);
|
||||
}
|
||||
|
||||
// Transition to object elements.
|
||||
{
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
ElementsKind target_kind = update_length == kBumpLengthWithGap
|
||||
? FAST_HOLEY_ELEMENTS
|
||||
: FAST_ELEMENTS;
|
||||
TryRewriteElements(receiver, receiver_map, elements, native_context,
|
||||
FAST_DOUBLE_ELEMENTS, target_kind, slow);
|
||||
// Reload migrated elements.
|
||||
Node* fast_elements = LoadElements(receiver);
|
||||
Node* fast_offset = ElementOffsetFromIndex(
|
||||
intptr_index, FAST_ELEMENTS, INTPTR_PARAMETERS, kHeaderSize);
|
||||
Store(MachineRepresentation::kTagged, fast_elements, fast_offset, value);
|
||||
MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&check_cow_elements);
|
||||
{
|
||||
// TODO(jkummerow): Use GrowElementsCapacity instead of bailing out.
|
||||
Goto(slow);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::EmitGenericElementStore(
|
||||
Node* receiver, Node* receiver_map, Node* instance_type, Node* intptr_index,
|
||||
Node* value, Node* context, Label* slow) {
|
||||
Label if_in_bounds(this), if_increment_length_by_one(this),
|
||||
if_bump_length_with_gap(this), if_grow(this), if_nonfast(this),
|
||||
if_typed_array(this), if_dictionary(this);
|
||||
Node* elements = LoadElements(receiver);
|
||||
Node* elements_kind = LoadMapElementsKind(receiver_map);
|
||||
GotoIf(
|
||||
Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
|
||||
&if_nonfast);
|
||||
|
||||
Label if_array(this);
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)), &if_array);
|
||||
{
|
||||
Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
|
||||
Branch(UintPtrLessThan(intptr_index, capacity), &if_in_bounds, &if_grow);
|
||||
}
|
||||
Bind(&if_array);
|
||||
{
|
||||
Node* length = SmiUntag(LoadJSArrayLength(receiver));
|
||||
GotoIf(UintPtrLessThan(intptr_index, length), &if_in_bounds);
|
||||
Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
|
||||
GotoIf(UintPtrGreaterThanOrEqual(intptr_index, capacity), &if_grow);
|
||||
Branch(WordEqual(intptr_index, length), &if_increment_length_by_one,
|
||||
&if_bump_length_with_gap);
|
||||
}
|
||||
|
||||
Bind(&if_in_bounds);
|
||||
{
|
||||
StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
|
||||
intptr_index, value, context, slow,
|
||||
kDontChangeLength);
|
||||
}
|
||||
|
||||
Bind(&if_increment_length_by_one);
|
||||
{
|
||||
StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
|
||||
intptr_index, value, context, slow,
|
||||
kIncrementLengthByOne);
|
||||
}
|
||||
|
||||
Bind(&if_bump_length_with_gap);
|
||||
{
|
||||
StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
|
||||
intptr_index, value, context, slow,
|
||||
kBumpLengthWithGap);
|
||||
}
|
||||
|
||||
// Out-of-capacity accesses (index >= capacity) jump here. Additionally,
|
||||
// an ElementsKind transition might be necessary.
|
||||
Bind(&if_grow);
|
||||
{
|
||||
Comment("Grow backing store");
|
||||
// TODO(jkummerow): Support inline backing store growth.
|
||||
Goto(slow);
|
||||
}
|
||||
|
||||
// Any ElementsKind > LAST_FAST_ELEMENTS_KIND jumps here for further dispatch.
|
||||
Bind(&if_nonfast);
|
||||
{
|
||||
STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
|
||||
GotoIf(Int32GreaterThanOrEqual(
|
||||
elements_kind,
|
||||
Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
|
||||
&if_typed_array);
|
||||
GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
|
||||
&if_dictionary);
|
||||
Goto(slow);
|
||||
}
|
||||
|
||||
Bind(&if_dictionary);
|
||||
{
|
||||
Comment("Dictionary");
|
||||
// TODO(jkummerow): Support storing to dictionary elements.
|
||||
Goto(slow);
|
||||
}
|
||||
|
||||
Bind(&if_typed_array);
|
||||
{
|
||||
Comment("Typed array");
|
||||
// TODO(jkummerow): Support typed arrays.
|
||||
Goto(slow);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
||||
Node* receiver, Node* receiver_map, const StoreICParameters* p,
|
||||
Label* slow) {
|
||||
Comment("stub cache probe");
|
||||
// TODO(jkummerow): Don't rely on the stub cache as much.
|
||||
// - existing properties can be overwritten inline (unless readonly).
|
||||
// - for dictionary mode receivers, we can even add properties inline
|
||||
// (unless the prototype chain prevents it).
|
||||
Variable var_handler(this, MachineRepresentation::kTagged);
|
||||
Label found_handler(this, &var_handler), stub_cache_miss(this);
|
||||
TryProbeStubCache(isolate()->store_stub_cache(), receiver, p->name,
|
||||
&found_handler, &var_handler, &stub_cache_miss);
|
||||
Bind(&found_handler);
|
||||
{
|
||||
Comment("KeyedStoreGeneric found handler");
|
||||
HandleStoreICHandlerCase(p, var_handler.value(), slow);
|
||||
}
|
||||
Bind(&stub_cache_miss);
|
||||
{
|
||||
Comment("KeyedStoreGeneric_miss");
|
||||
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
|
||||
p->vector, p->receiver, p->name);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::KeyedStoreGeneric(const StoreICParameters* p,
|
||||
LanguageMode language_mode) {
|
||||
Variable var_index(this, MachineType::PointerRepresentation());
|
||||
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, &slow);
|
||||
|
||||
Bind(&if_index);
|
||||
{
|
||||
Comment("integer index");
|
||||
EmitGenericElementStore(receiver, receiver_map, instance_type,
|
||||
var_index.value(), p->value, p->context, &slow);
|
||||
}
|
||||
|
||||
Bind(&if_unique_name);
|
||||
{
|
||||
Comment("key is unique name");
|
||||
EmitGenericPropertyStore(receiver, receiver_map, p, &slow);
|
||||
}
|
||||
|
||||
Bind(&slow);
|
||||
{
|
||||
Comment("KeyedStoreGeneric_slow");
|
||||
TailCallRuntime(Runtime::kSetProperty, p->context, p->receiver, p->name,
|
||||
p->value, SmiConstant(language_mode));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
23
src/ic/keyed-store-generic.h
Normal file
23
src/ic/keyed-store-generic.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_SRC_IC_KEYED_STORE_GENERIC_H_
|
||||
#define V8_SRC_IC_KEYED_STORE_GENERIC_H_
|
||||
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class KeyedStoreGenericGenerator {
|
||||
public:
|
||||
static void Generate(CodeStubAssembler* assembler,
|
||||
const CodeStubAssembler::StoreICParameters* p,
|
||||
LanguageMode language_mode);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_SRC_IC_KEYED_STORE_GENERIC_H_
|
@ -1670,18 +1670,6 @@ AllocationSiteMode AllocationSite::GetMode(
|
||||
return DONT_TRACK_ALLOCATION_SITE;
|
||||
}
|
||||
|
||||
|
||||
AllocationSiteMode AllocationSite::GetMode(ElementsKind from,
|
||||
ElementsKind to) {
|
||||
if (IsFastSmiElementsKind(from) &&
|
||||
IsMoreGeneralElementsKindTransition(from, to)) {
|
||||
return TRACK_ALLOCATION_SITE;
|
||||
}
|
||||
|
||||
return DONT_TRACK_ALLOCATION_SITE;
|
||||
}
|
||||
|
||||
|
||||
inline bool AllocationSite::CanTrack(InstanceType type) {
|
||||
if (FLAG_allocation_site_pretenuring) {
|
||||
return type == JS_ARRAY_TYPE ||
|
||||
|
@ -15937,6 +15937,14 @@ bool AllocationSite::DigestTransitionFeedback(Handle<AllocationSite> site,
|
||||
return result;
|
||||
}
|
||||
|
||||
AllocationSiteMode AllocationSite::GetMode(ElementsKind from, ElementsKind to) {
|
||||
if (IsFastSmiElementsKind(from) &&
|
||||
IsMoreGeneralElementsKindTransition(from, to)) {
|
||||
return TRACK_ALLOCATION_SITE;
|
||||
}
|
||||
|
||||
return DONT_TRACK_ALLOCATION_SITE;
|
||||
}
|
||||
|
||||
const char* AllocationSite::PretenureDecisionName(PretenureDecision decision) {
|
||||
switch (decision) {
|
||||
|
@ -9203,7 +9203,7 @@ class AllocationSite: public Struct {
|
||||
DECLARE_CAST(AllocationSite)
|
||||
static inline AllocationSiteMode GetMode(
|
||||
ElementsKind boilerplate_elements_kind);
|
||||
static inline AllocationSiteMode GetMode(ElementsKind from, ElementsKind to);
|
||||
static AllocationSiteMode GetMode(ElementsKind from, ElementsKind to);
|
||||
static inline bool CanTrack(InstanceType type);
|
||||
|
||||
static const int kTransitionInfoOffset = HeapObject::kHeaderSize;
|
||||
|
@ -960,6 +960,8 @@
|
||||
'ic/ic.h',
|
||||
'ic/ic-compiler.cc',
|
||||
'ic/ic-compiler.h',
|
||||
'ic/keyed-store-generic.cc',
|
||||
'ic/keyed-store-generic.h',
|
||||
'identity-map.cc',
|
||||
'identity-map.h',
|
||||
'interface-descriptors.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user