Commit 9be74766 authored by jkummerow's avatar jkummerow Committed by Commit bot

Reland^2 of [stubs] KeyedStoreGeneric: inline dictionary property stores

For dictionary-mode receivers, the KeyedStoreGeneric stub can store
properties directly in most cases. Doing so avoids the need to have
an entry in the stub cache for every map/property combination.

Original review: https://codereview.chromium.org/2504403005/

Review-Url: https://codereview.chromium.org/2528883003
Cr-Commit-Position: refs/heads/master@{#41272}
parent c4ccbaa3
......@@ -2566,10 +2566,9 @@ void Builtins::Generate_ArrayIteratorPrototypeNext(
Node* elements =
assembler.AllocateFixedArray(FAST_ELEMENTS, assembler.Int32Constant(2));
assembler.StoreFixedArrayElement(elements, assembler.Int32Constant(0),
index, SKIP_WRITE_BARRIER);
assembler.StoreFixedArrayElement(elements, assembler.Int32Constant(1),
var_value.value(), SKIP_WRITE_BARRIER);
assembler.StoreFixedArrayElement(elements, 0, index, SKIP_WRITE_BARRIER);
assembler.StoreFixedArrayElement(elements, 1, var_value.value(),
SKIP_WRITE_BARRIER);
Node* entry = assembler.Allocate(JSArray::kSize);
Node* map =
......
......@@ -293,8 +293,7 @@ compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate,
a->AllocateRegExpResult(context, num_results, start, string);
Node* const result_elements = a->LoadElements(result);
a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first,
SKIP_WRITE_BARRIER);
a->StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out);
......@@ -1846,7 +1845,7 @@ compiler::Node* ReplaceGlobalCallableFastPath(
Node* const replacement_str = a->ToString(context, replacement_obj);
a->StoreFixedArrayElement(res_elems, index, replacement_str,
UPDATE_WRITE_BARRIER, mode);
UPDATE_WRITE_BARRIER, 0, mode);
a->Goto(&do_continue);
a->Bind(&do_continue);
......
......@@ -1336,12 +1336,14 @@ Node* CodeStubAssembler::StoreObjectFieldRoot(Node* object, int offset,
Node* CodeStubAssembler::StoreFixedArrayElement(Node* object, Node* index_node,
Node* value,
WriteBarrierMode barrier_mode,
int additional_offset,
ParameterMode parameter_mode) {
DCHECK(barrier_mode == SKIP_WRITE_BARRIER ||
barrier_mode == UPDATE_WRITE_BARRIER);
Node* offset =
ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS, parameter_mode,
FixedArray::kHeaderSize - kHeapObjectTag);
int header_size =
FixedArray::kHeaderSize + additional_offset - kHeapObjectTag;
Node* offset = ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS,
parameter_mode, header_size);
MachineRepresentation rep = MachineRepresentation::kTagged;
if (barrier_mode == SKIP_WRITE_BARRIER) {
return StoreNoWriteBarrier(rep, object, offset, value);
......@@ -4036,19 +4038,62 @@ Node* CodeStubAssembler::IntPtrMax(Node* left, Node* right) {
MachineType::PointerRepresentation());
}
template <class Dictionary>
Node* CodeStubAssembler::GetNumberOfElements(Node* dictionary) {
return LoadFixedArrayElement(
dictionary, IntPtrConstant(Dictionary::kNumberOfElementsIndex), 0,
INTPTR_PARAMETERS);
}
template <class Dictionary>
void CodeStubAssembler::SetNumberOfElements(Node* dictionary,
Node* num_elements_smi) {
StoreFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex,
num_elements_smi, SKIP_WRITE_BARRIER);
}
template <class Dictionary>
Node* CodeStubAssembler::GetNumberOfDeletedElements(Node* dictionary) {
return LoadFixedArrayElement(
dictionary, IntPtrConstant(Dictionary::kNumberOfDeletedElementsIndex), 0,
INTPTR_PARAMETERS);
}
template <class Dictionary>
Node* CodeStubAssembler::GetCapacity(Node* dictionary) {
return LoadFixedArrayElement(dictionary,
IntPtrConstant(Dictionary::kCapacityIndex), 0,
INTPTR_PARAMETERS);
}
template <class Dictionary>
Node* CodeStubAssembler::GetNextEnumerationIndex(Node* dictionary) {
return LoadFixedArrayElement(
dictionary, IntPtrConstant(Dictionary::kNextEnumerationIndexIndex), 0,
INTPTR_PARAMETERS);
}
template <class Dictionary>
void CodeStubAssembler::SetNextEnumerationIndex(Node* dictionary,
Node* next_enum_index_smi) {
StoreFixedArrayElement(dictionary, Dictionary::kNextEnumerationIndexIndex,
next_enum_index_smi, SKIP_WRITE_BARRIER);
}
template <typename Dictionary>
void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
Node* unique_name, Label* if_found,
Variable* var_name_index,
Label* if_not_found,
int inlined_probes) {
int inlined_probes,
LookupMode mode) {
CSA_ASSERT(this, IsDictionary(dictionary));
DCHECK_EQ(MachineType::PointerRepresentation(), var_name_index->rep());
DCHECK_IMPLIES(mode == kFindInsertionIndex,
inlined_probes == 0 && if_found == nullptr);
Comment("NameDictionaryLookup");
Node* capacity = SmiUntag(LoadFixedArrayElement(
dictionary, IntPtrConstant(Dictionary::kCapacityIndex), 0,
INTPTR_PARAMETERS));
Node* capacity = SmiUntag(GetCapacity<Dictionary>(dictionary));
Node* mask = IntPtrSub(capacity, IntPtrConstant(1));
Node* hash = ChangeUint32ToWord(LoadNameHash(unique_name));
......@@ -4068,8 +4113,13 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
count = IntPtrConstant(i + 1);
entry = WordAnd(IntPtrAdd(entry, count), mask);
}
if (mode == kFindInsertionIndex) {
// Appease the variable merging algorithm for "Goto(&loop)" below.
var_name_index->Bind(IntPtrConstant(0));
}
Node* undefined = UndefinedConstant();
Node* the_hole = mode == kFindExisting ? nullptr : TheHoleConstant();
Variable var_count(this, MachineType::PointerRepresentation());
Variable var_entry(this, MachineType::PointerRepresentation());
......@@ -4089,7 +4139,12 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
Node* current =
LoadFixedArrayElement(dictionary, index, 0, INTPTR_PARAMETERS);
GotoIf(WordEqual(current, undefined), if_not_found);
if (mode == kFindExisting) {
GotoIf(WordEqual(current, unique_name), if_found);
} else {
DCHECK_EQ(kFindInsertionIndex, mode);
GotoIf(WordEqual(current, the_hole), if_not_found);
}
// See Dictionary::NextProbe().
count = IntPtrAdd(count, IntPtrConstant(1));
......@@ -4103,9 +4158,9 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
// Instantiate template methods to workaround GCC compilation issue.
template void CodeStubAssembler::NameDictionaryLookup<NameDictionary>(
Node*, Node*, Label*, Variable*, Label*, int);
Node*, Node*, Label*, Variable*, Label*, int, LookupMode);
template void CodeStubAssembler::NameDictionaryLookup<GlobalDictionary>(
Node*, Node*, Label*, Variable*, Label*, int);
Node*, Node*, Label*, Variable*, Label*, int, LookupMode);
Node* CodeStubAssembler::ComputeIntegerHash(Node* key, Node* seed) {
// See v8::internal::ComputeIntegerHash()
......@@ -4131,9 +4186,7 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary,
DCHECK_EQ(MachineType::PointerRepresentation(), var_entry->rep());
Comment("NumberDictionaryLookup");
Node* capacity = SmiUntag(LoadFixedArrayElement(
dictionary, IntPtrConstant(Dictionary::kCapacityIndex), 0,
INTPTR_PARAMETERS));
Node* capacity = SmiUntag(GetCapacity<Dictionary>(dictionary));
Node* mask = IntPtrSub(capacity, IntPtrConstant(1));
Node* int32_seed;
......@@ -4197,6 +4250,121 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary,
}
}
template <class Dictionary>
void CodeStubAssembler::FindInsertionEntry(Node* dictionary, Node* key,
Variable* var_key_index) {
UNREACHABLE();
}
template <>
void CodeStubAssembler::FindInsertionEntry<NameDictionary>(
Node* dictionary, Node* key, Variable* var_key_index) {
Label done(this);
NameDictionaryLookup<NameDictionary>(dictionary, key, nullptr, var_key_index,
&done, 0, kFindInsertionIndex);
Bind(&done);
}
template <class Dictionary>
void CodeStubAssembler::InsertEntry(Node* dictionary, Node* key, Node* value,
Node* index, Node* enum_index) {
UNREACHABLE(); // Use specializations instead.
}
template <>
void CodeStubAssembler::InsertEntry<NameDictionary>(Node* dictionary,
Node* name, Node* value,
Node* index,
Node* enum_index) {
// Store name and value.
StoreFixedArrayElement(dictionary, index, name, UPDATE_WRITE_BARRIER, 0,
INTPTR_PARAMETERS);
const int kNameToValueOffset =
(NameDictionary::kEntryValueIndex - NameDictionary::kEntryKeyIndex) *
kPointerSize;
StoreFixedArrayElement(dictionary, index, value, UPDATE_WRITE_BARRIER,
kNameToValueOffset, INTPTR_PARAMETERS);
// Prepare details of the new property.
Variable var_details(this, MachineRepresentation::kTaggedSigned);
const int kInitialIndex = 0;
PropertyDetails d(NONE, DATA, kInitialIndex, PropertyCellType::kNoCell);
enum_index =
WordShl(enum_index, PropertyDetails::DictionaryStorageField::kShift);
STATIC_ASSERT(kInitialIndex == 0);
var_details.Bind(WordOr(SmiConstant(d.AsSmi()), enum_index));
// Private names must be marked non-enumerable.
Label not_private(this, &var_details);
GotoUnless(IsSymbolMap(LoadMap(name)), &not_private);
Node* flags = SmiToWord32(LoadObjectField(name, Symbol::kFlagsOffset));
const int kPrivateMask = 1 << Symbol::kPrivateBit;
GotoUnless(IsSetWord32(flags, kPrivateMask), &not_private);
Node* dont_enum =
WordShl(SmiConstant(DONT_ENUM), PropertyDetails::AttributesField::kShift);
var_details.Bind(WordOr(var_details.value(), dont_enum));
Goto(&not_private);
Bind(&not_private);
// Finally, store the details.
const int kNameToDetailsOffset =
(NameDictionary::kEntryDetailsIndex - NameDictionary::kEntryKeyIndex) *
kPointerSize;
StoreFixedArrayElement(dictionary, index, var_details.value(),
SKIP_WRITE_BARRIER, kNameToDetailsOffset,
INTPTR_PARAMETERS);
}
template <>
void CodeStubAssembler::InsertEntry<GlobalDictionary>(Node* dictionary,
Node* key, Node* value,
Node* index,
Node* enum_index) {
UNIMPLEMENTED();
}
template <class Dictionary>
void CodeStubAssembler::Add(Node* dictionary, Node* key, Node* value,
Label* bailout) {
Node* capacity = GetCapacity<Dictionary>(dictionary);
Node* nof = GetNumberOfElements<Dictionary>(dictionary);
Node* new_nof = SmiAdd(nof, SmiConstant(1));
// Require 33% to still be free after adding additional_elements.
// Computing "x + (x >> 1)" on a Smi x does not return a valid Smi!
// But that's OK here because it's only used for a comparison.
Node* required_capacity_pseudo_smi = SmiAdd(new_nof, WordShr(new_nof, 1));
GotoIf(UintPtrLessThan(capacity, required_capacity_pseudo_smi), bailout);
// Require rehashing if more than 50% of free elements are deleted elements.
Node* deleted = GetNumberOfDeletedElements<Dictionary>(dictionary);
CSA_ASSERT(this, UintPtrGreaterThan(capacity, new_nof));
Node* half_of_free_elements = WordShr(SmiSub(capacity, new_nof), 1);
GotoIf(UintPtrGreaterThan(deleted, half_of_free_elements), bailout);
Node* enum_index = nullptr;
if (Dictionary::kIsEnumerable) {
enum_index = GetNextEnumerationIndex<Dictionary>(dictionary);
Node* new_enum_index = SmiAdd(enum_index, SmiConstant(1));
Node* max_enum_index =
SmiConstant(PropertyDetails::DictionaryStorageField::kMax);
GotoIf(UintPtrGreaterThan(new_enum_index, max_enum_index), bailout);
// No more bailouts after this point.
// Operations from here on can have side effects.
SetNextEnumerationIndex<Dictionary>(dictionary, new_enum_index);
} else {
USE(enum_index);
}
SetNumberOfElements<Dictionary>(dictionary, new_nof);
Variable var_key_index(this, MachineType::PointerRepresentation());
FindInsertionEntry<Dictionary>(dictionary, key, &var_key_index);
InsertEntry<Dictionary>(dictionary, key, value, var_key_index.value(),
enum_index);
}
template void CodeStubAssembler::Add<NameDictionary>(Node*, Node*, Node*,
Label*);
void CodeStubAssembler::DescriptorLookupLinear(Node* unique_name,
Node* descriptors, Node* nof,
Label* if_found,
......@@ -5194,7 +5362,7 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(Node* receiver, Node* key,
var_result.Bind(result);
} else {
StoreFixedArrayElement(the_context, mapped_index, value,
UPDATE_WRITE_BARRIER, INTPTR_PARAMETERS);
UPDATE_WRITE_BARRIER, 0, INTPTR_PARAMETERS);
}
Goto(&end);
}
......@@ -5217,7 +5385,7 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(Node* receiver, Node* key,
GotoIf(WordEqual(result, TheHoleConstant()), bailout);
var_result.Bind(result);
} else {
StoreFixedArrayElement(backing_store, key, value, UPDATE_WRITE_BARRIER,
StoreFixedArrayElement(backing_store, key, value, UPDATE_WRITE_BARRIER, 0,
INTPTR_PARAMETERS);
}
Goto(&end);
......@@ -5300,7 +5468,7 @@ void CodeStubAssembler::StoreElement(Node* elements, ElementsKind kind,
value = Float64SilenceNaN(value);
StoreFixedDoubleArrayElement(elements, index, value, mode);
} else {
StoreFixedArrayElement(elements, index, value, barrier_mode, mode);
StoreFixedArrayElement(elements, index, value, barrier_mode, 0, mode);
}
}
......@@ -5692,7 +5860,7 @@ Node* CodeStubAssembler::CreateAllocationSiteInFeedbackVector(
StoreObjectField(site, AllocationSite::kWeakNextOffset, next_site);
StoreNoWriteBarrier(MachineRepresentation::kTagged, site_list, site);
StoreFixedArrayElement(feedback_vector, slot, site, UPDATE_WRITE_BARRIER,
StoreFixedArrayElement(feedback_vector, slot, site, UPDATE_WRITE_BARRIER, 0,
CodeStubAssembler::SMI_PARAMETERS);
return site;
}
......@@ -5710,7 +5878,7 @@ Node* CodeStubAssembler::CreateWeakCellInFeedbackVector(Node* feedback_vector,
Heap::kTheHoleValueRootIndex);
// Store the WeakCell in the feedback vector.
StoreFixedArrayElement(feedback_vector, slot, cell, UPDATE_WRITE_BARRIER,
StoreFixedArrayElement(feedback_vector, slot, cell, UPDATE_WRITE_BARRIER, 0,
CodeStubAssembler::SMI_PARAMETERS);
return cell;
}
......
......@@ -21,6 +21,7 @@ class StubCache;
enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
#define HEAP_CONSTANT_LIST(V) \
V(AccessorInfoMap, AccessorInfoMap) \
V(BooleanMap, BooleanMap) \
V(CodeMap, CodeMap) \
V(empty_string, EmptyString) \
......@@ -29,10 +30,12 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(FixedArrayMap, FixedArrayMap) \
V(FixedCOWArrayMap, FixedCOWArrayMap) \
V(FixedDoubleArrayMap, FixedDoubleArrayMap) \
V(FunctionTemplateInfoMap, FunctionTemplateInfoMap) \
V(HeapNumberMap, HeapNumberMap) \
V(MinusZeroValue, MinusZero) \
V(NanValue, Nan) \
V(NullValue, Null) \
V(SymbolMap, SymbolMap) \
V(TheHoleValue, TheHole) \
V(TrueValue, True) \
V(UndefinedValue, Undefined)
......@@ -357,15 +360,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Store an array element to a FixedArray.
compiler::Node* StoreFixedArrayElement(
compiler::Node* object, int index, compiler::Node* value,
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
ParameterMode parameter_mode = INTEGER_PARAMETERS) {
return StoreFixedArrayElement(object, Int32Constant(index), value,
barrier_mode, parameter_mode);
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER) {
return StoreFixedArrayElement(object, IntPtrConstant(index), value,
barrier_mode, 0, INTPTR_PARAMETERS);
}
compiler::Node* StoreFixedArrayElement(
compiler::Node* object, compiler::Node* index, compiler::Node* value,
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
int additional_offset = 0,
ParameterMode parameter_mode = INTEGER_PARAMETERS);
compiler::Node* StoreFixedDoubleArrayElement(
......@@ -762,16 +765,38 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Calculate a valid size for the a hash table.
compiler::Node* HashTableComputeCapacity(compiler::Node* at_least_space_for);
template <class Dictionary>
compiler::Node* GetNumberOfElements(compiler::Node* dictionary);
template <class Dictionary>
void SetNumberOfElements(compiler::Node* dictionary,
compiler::Node* num_elements_smi);
template <class Dictionary>
compiler::Node* GetNumberOfDeletedElements(compiler::Node* dictionary);
template <class Dictionary>
compiler::Node* GetCapacity(compiler::Node* dictionary);
template <class Dictionary>
compiler::Node* GetNextEnumerationIndex(compiler::Node* dictionary);
template <class Dictionary>
void SetNextEnumerationIndex(compiler::Node* dictionary,
compiler::Node* next_enum_index_smi);
// Looks up an entry in a NameDictionaryBase successor. If the entry is found
// control goes to {if_found} and {var_name_index} contains an index of the
// key field of the entry found. If the key is not found control goes to
// {if_not_found}.
static const int kInlinedDictionaryProbes = 4;
enum LookupMode { kFindExisting, kFindInsertionIndex };
template <typename Dictionary>
void NameDictionaryLookup(compiler::Node* dictionary,
compiler::Node* unique_name, Label* if_found,
Variable* var_name_index, Label* if_not_found,
int inlined_probes = kInlinedDictionaryProbes);
int inlined_probes = kInlinedDictionaryProbes,
LookupMode mode = kFindExisting);
compiler::Node* ComputeIntegerHash(compiler::Node* key, compiler::Node* seed);
......@@ -780,6 +805,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* intptr_index, Label* if_found,
Variable* var_entry, Label* if_not_found);
template <class Dictionary>
void FindInsertionEntry(compiler::Node* dictionary, compiler::Node* key,
Variable* var_key_index);
template <class Dictionary>
void InsertEntry(compiler::Node* dictionary, compiler::Node* key,
compiler::Node* value, compiler::Node* index,
compiler::Node* enum_index);
template <class Dictionary>
void Add(compiler::Node* dictionary, compiler::Node* key,
compiler::Node* value, Label* bailout);
// Tries to check if {object} has own {unique_name} property.
void TryHasOwnProperty(compiler::Node* object, compiler::Node* map,
compiler::Node* instance_type,
......
......@@ -2117,7 +2117,7 @@ void StoreScriptContextFieldStub::GenerateAssembly(
Node* script_context = assembler.LoadScriptContext(context, context_index());
assembler.StoreFixedArrayElement(
script_context, assembler.IntPtrConstant(slot_index()), value,
UPDATE_WRITE_BARRIER, CodeStubAssembler::INTPTR_PARAMETERS);
UPDATE_WRITE_BARRIER, 0, CodeStubAssembler::INTPTR_PARAMETERS);
assembler.Return(value);
}
......@@ -2621,20 +2621,18 @@ compiler::Node* FastNewFunctionContextStub::Generate(
assembler->SmiFromWord32(length));
// Set up the fixed slots.
assembler->StoreFixedArrayElement(
function_context, assembler->Int32Constant(Context::CLOSURE_INDEX),
assembler->StoreFixedArrayElement(function_context, Context::CLOSURE_INDEX,
function, SKIP_WRITE_BARRIER);
assembler->StoreFixedArrayElement(
function_context, assembler->Int32Constant(Context::PREVIOUS_INDEX),
assembler->StoreFixedArrayElement(function_context, Context::PREVIOUS_INDEX,
context, SKIP_WRITE_BARRIER);
assembler->StoreFixedArrayElement(
function_context, assembler->Int32Constant(Context::EXTENSION_INDEX),
assembler->TheHoleConstant(), SKIP_WRITE_BARRIER);
assembler->StoreFixedArrayElement(function_context, Context::EXTENSION_INDEX,
assembler->TheHoleConstant(),
SKIP_WRITE_BARRIER);
// Copy the native context from the previous context.
Node* native_context = assembler->LoadNativeContext(context);
assembler->StoreFixedArrayElement(
function_context, assembler->Int32Constant(Context::NATIVE_CONTEXT_INDEX),
assembler->StoreFixedArrayElement(function_context,
Context::NATIVE_CONTEXT_INDEX,
native_context, SKIP_WRITE_BARRIER);
// Initialize the rest of the slots to undefined.
......
......@@ -1367,9 +1367,8 @@ void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
const int32_t kMaxLinear = 210;
Label stub_cache(this);
Node* bitfield3 = LoadMapBitField3(receiver_map);
Node* nof =
DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
GotoIf(UintPtrGreaterThan(nof, IntPtrConstant(kMaxLinear)), &stub_cache);
Node* nof = DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
GotoIf(Uint32LessThan(Int32Constant(kMaxLinear), nof), &stub_cache);
Node* descriptors = LoadMapDescriptors(receiver_map);
Variable var_name_index(this, MachineType::PointerRepresentation());
Label if_descriptor_found(this);
......
......@@ -4,6 +4,7 @@
#include "src/ic/keyed-store-generic.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/contexts.h"
#include "src/ic/accessor-assembler-impl.h"
......@@ -34,7 +35,8 @@ class KeyedStoreGenericAssembler : public AccessorAssemblerImpl {
Node* value, Node* context, Label* slow);
void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
const StoreICParameters* p, Label* slow);
const StoreICParameters* p, Label* slow,
LanguageMode language_mode);
void BranchIfPrototypesHaveNonFastElements(Node* receiver_map,
Label* non_fast_elements,
......@@ -63,6 +65,13 @@ class KeyedStoreGenericAssembler : public AccessorAssemblerImpl {
Node* current_elements_kind, Node* context,
ElementsKind packed_kind,
ElementsKind packed_kind_2, Label* bailout);
void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
void LookupPropertyOnPrototypeChain(Node* receiver_map, Node* name,
Label* accessor,
Variable* var_accessor_pair,
Variable* var_accessor_holder,
Label* readonly, Label* bailout);
};
void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state,
......@@ -478,14 +487,238 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
}
}
void KeyedStoreGenericAssembler::JumpIfDataProperty(Node* details,
Label* writable,
Label* readonly) {
// Accessor properties never have the READ_ONLY attribute set.
GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
readonly);
Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
// Fall through if it's an accessor property.
}
void KeyedStoreGenericAssembler::LookupPropertyOnPrototypeChain(
Node* receiver_map, Node* name, Label* accessor,
Variable* var_accessor_pair, Variable* var_accessor_holder, Label* readonly,
Label* bailout) {
Label ok_to_write(this);
Variable var_holder(this, MachineRepresentation::kTagged);
var_holder.Bind(LoadMapPrototype(receiver_map));
Variable var_holder_map(this, MachineRepresentation::kTagged);
var_holder_map.Bind(LoadMap(var_holder.value()));
Variable* merged_variables[] = {&var_holder, &var_holder_map};
Label loop(this, arraysize(merged_variables), merged_variables);
Goto(&loop);
Bind(&loop);
{
Node* holder = var_holder.value();
Node* holder_map = var_holder_map.value();
Node* instance_type = LoadMapInstanceType(holder_map);
Label next_proto(this);
{
Label found(this), found_fast(this), found_dict(this), found_global(this);
Variable var_meta_storage(this, MachineRepresentation::kTagged);
Variable var_entry(this, MachineType::PointerRepresentation());
TryLookupProperty(holder, holder_map, instance_type, name, &found_fast,
&found_dict, &found_global, &var_meta_storage,
&var_entry, &next_proto, bailout);
Bind(&found_fast);
{
Node* descriptors = var_meta_storage.value();
Node* name_index = var_entry.value();
// TODO(jkummerow): Add helper functions for accessing value and
// details by entry.
const int kNameToDetailsOffset = (DescriptorArray::kDescriptorDetails -
DescriptorArray::kDescriptorKey) *
kPointerSize;
Node* details = LoadAndUntagToWord32FixedArrayElement(
descriptors, name_index, kNameToDetailsOffset);
JumpIfDataProperty(details, &ok_to_write, readonly);
// Accessor case.
Variable var_details(this, MachineRepresentation::kWord32);
LoadPropertyFromFastObject(holder, holder_map, descriptors, name_index,
&var_details, var_accessor_pair);
var_accessor_holder->Bind(holder);
Goto(accessor);
}
Bind(&found_dict);
{
Node* dictionary = var_meta_storage.value();
Node* entry = var_entry.value();
const int kNameToDetailsOffset = (NameDictionary::kEntryDetailsIndex -
NameDictionary::kEntryKeyIndex) *
kPointerSize;
Node* details = LoadAndUntagToWord32FixedArrayElement(
dictionary, entry, kNameToDetailsOffset);
JumpIfDataProperty(details, &ok_to_write, readonly);
// Accessor case.
const int kNameToValueOffset = (NameDictionary::kEntryValueIndex -
NameDictionary::kEntryKeyIndex) *
kPointerSize;
var_accessor_pair->Bind(
LoadFixedArrayElement(dictionary, entry, kNameToValueOffset));
var_accessor_holder->Bind(holder);
Goto(accessor);
}
Bind(&found_global);
{
Node* dictionary = var_meta_storage.value();
Node* entry = var_entry.value();
const int kNameToValueOffset = (GlobalDictionary::kEntryValueIndex -
GlobalDictionary::kEntryKeyIndex) *
kPointerSize;
Node* property_cell =
LoadFixedArrayElement(dictionary, entry, kNameToValueOffset);
Node* value =
LoadObjectField(property_cell, PropertyCell::kValueOffset);
GotoIf(WordEqual(value, TheHoleConstant()), &next_proto);
Node* details = LoadAndUntagToWord32ObjectField(
property_cell, PropertyCell::kDetailsOffset);
JumpIfDataProperty(details, &ok_to_write, readonly);
// Accessor case.
var_accessor_pair->Bind(value);
var_accessor_holder->Bind(holder);
Goto(accessor);
}
}
Bind(&next_proto);
// Bailout if it can be an integer indexed exotic case.
GotoIf(Word32Equal(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
bailout);
Node* proto = LoadMapPrototype(holder_map);
GotoIf(WordEqual(proto, NullConstant()), &ok_to_write);
var_holder.Bind(proto);
var_holder_map.Bind(LoadMap(proto));
Goto(&loop);
}
Bind(&ok_to_write);
}
void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
Node* receiver, Node* receiver_map, const StoreICParameters* p,
Label* slow) {
Node* receiver, Node* receiver_map, const StoreICParameters* p, Label* slow,
LanguageMode language_mode) {
Variable var_accessor_pair(this, MachineRepresentation::kTagged);
Variable var_accessor_holder(this, MachineRepresentation::kTagged);
Label stub_cache(this), fast_properties(this), dictionary_properties(this),
accessor(this), readonly(this);
Node* properties = LoadProperties(receiver);
Node* properties_map = LoadMap(properties);
Branch(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
&dictionary_properties, &fast_properties);
Bind(&fast_properties);
{
// TODO(jkummerow): Does it make sense to support some cases here inline?
// Maybe overwrite existing writable properties?
// Maybe support map transitions?
Goto(&stub_cache);
}
Bind(&dictionary_properties);
{
Comment("dictionary property store");
// 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), not_found(this);
NameDictionaryLookup<NameDictionary>(properties, p->name, &dictionary_found,
&var_name_index, &not_found);
Bind(&dictionary_found);
{
Label overwrite(this);
const int kNameToDetailsOffset = (NameDictionary::kEntryDetailsIndex -
NameDictionary::kEntryKeyIndex) *
kPointerSize;
Node* details = LoadAndUntagToWord32FixedArrayElement(
properties, var_name_index.value(), kNameToDetailsOffset);
JumpIfDataProperty(details, &overwrite, &readonly);
// Accessor case.
const int kNameToValueOffset =
(NameDictionary::kEntryValueIndex - NameDictionary::kEntryKeyIndex) *
kPointerSize;
var_accessor_pair.Bind(LoadFixedArrayElement(
properties, var_name_index.value(), kNameToValueOffset));
var_accessor_holder.Bind(receiver);
Goto(&accessor);
Bind(&overwrite);
{
StoreFixedArrayElement(properties, var_name_index.value(), p->value,
UPDATE_WRITE_BARRIER, kNameToValueOffset,
INTPTR_PARAMETERS);
Return(p->value);
}
}
Bind(&not_found);
{
LookupPropertyOnPrototypeChain(receiver_map, p->name, &accessor,
&var_accessor_pair, &var_accessor_holder,
&readonly, slow);
Add<NameDictionary>(properties, p->name, p->value, slow);
Return(p->value);
}
}
Bind(&accessor);
{
Label not_callable(this);
Node* accessor_pair = var_accessor_pair.value();
GotoIf(IsAccessorInfoMap(LoadMap(accessor_pair)), slow);
CSA_ASSERT(this, HasInstanceType(accessor_pair, ACCESSOR_PAIR_TYPE));
Node* setter = LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
Node* setter_map = LoadMap(setter);
// FunctionTemplateInfo setters are not supported yet.
GotoIf(IsFunctionTemplateInfoMap(setter_map), slow);
GotoUnless(IsCallableMap(setter_map), &not_callable);
Callable callable = CodeFactory::Call(isolate());
CallJS(callable, p->context, setter, receiver, p->value);
Return(p->value);
Bind(&not_callable);
{
if (language_mode == STRICT) {
Node* message =
SmiConstant(Smi::FromInt(MessageTemplate::kNoSetterInCallback));
TailCallRuntime(Runtime::kThrowTypeError, p->context, message, p->name,
var_accessor_holder.value());
} else {
DCHECK_EQ(SLOPPY, language_mode);
Return(p->value);
}
}
}
Bind(&readonly);
{
if (language_mode == STRICT) {
Node* message =
SmiConstant(Smi::FromInt(MessageTemplate::kStrictReadOnlyProperty));
Node* type = Typeof(p->receiver, p->context);
TailCallRuntime(Runtime::kThrowTypeError, p->context, message, p->name,
type, p->receiver);
} else {
DCHECK_EQ(SLOPPY, language_mode);
Return(p->value);
}
}
Bind(&stub_cache);
{
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,
......@@ -498,8 +731,9 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
Bind(&stub_cache_miss);
{
Comment("KeyedStoreGeneric_miss");
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
p->vector, p->receiver, p->name);
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value,
p->slot, p->vector, p->receiver, p->name);
}
}
}
......@@ -539,7 +773,7 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
Comment("key is unique name");
KeyedStoreGenericAssembler::StoreICParameters p(context, receiver, name,
value, slot, vector);
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow);
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow, language_mode);
}
Bind(&slow);
......
......@@ -3851,6 +3851,8 @@ class Dictionary: public HashTable<Derived, Shape, Key> {
static const int kMaxNumberKeyIndex = DerivedHashTable::kPrefixStartIndex;
static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1;
static const bool kIsEnumerable = Shape::kIsEnumerable;
protected:
// Generic at put operation.
MUST_USE_RESULT static Handle<Derived> AtPut(
......@@ -9595,14 +9597,15 @@ class Symbol: public Name {
static const int kFlagsOffset = kNameOffset + kPointerSize;
static const int kSize = kFlagsOffset + kPointerSize;
// Flags layout.
static const int kPrivateBit = 0;
static const int kWellKnownSymbolBit = 1;
typedef FixedBodyDescriptor<kNameOffset, kFlagsOffset, kSize> BodyDescriptor;
void SymbolShortPrint(std::ostream& os);
private:
static const int kPrivateBit = 0;
static const int kWellKnownSymbolBit = 1;
const char* PrivateSymbolToName() const;
#if TRACE_MAPS
......
// 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.
// Flags: --allow-natives-syntax
function f(a, i, v) {
a[i] = v;
}
f("make it generic", 0, 0);
var a = new Array(3);
// Fast properties.
f(a, "length", 2);
assertEquals(2, a.length);
// Dictionary properties.
%OptimizeObjectForAddingMultipleProperties(a, 1);
f(a, "length", 1);
assertEquals(1, a.length);
......@@ -2,7 +2,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
class A {}
class B {}
Object.assign(B, A);
assertEquals("class B {}", B.toString());
(function() {
function f(a, i, v) {
a[i] = v;
}
f("make it generic", 0, 0);
var o = {foo: "foo"};
%OptimizeObjectForAddingMultipleProperties(o, 10);
var s = %CreatePrivateSymbol("priv");
f(o, s, "private");
%ToFastProperties(o);
var desc = Object.getOwnPropertyDescriptor(o, s);
assertEquals("private", desc.value);
assertTrue(desc.writable);
assertFalse(desc.enumerable);
assertTrue(desc.configurable);
})();
......@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax
// Test dictionary store ICs.
// Function that stores property 'x' on an object.
......@@ -62,3 +64,27 @@ assertEquals(42, o.x);
delete o.x;
store(o);
assertEquals(42, o.x);
(function() {
var o = {};
for (var i = 0; i < 100; i++) {
var k = "key" + i;
o[k] = "foo";
delete o[k];
}
})();
(function() {
function f(a, i, v) {
a[i] = v;
}
f("make it generic", 0, 0);
var o = {};
for (var i = 0; i < 100; i++) {
var k = %InternalizeString("key" + i);
f(o, k, "foo");
delete o[k];
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment