HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Android 10
|
10.0.0_r6
下载
查看原文件
收藏
根目录
external
v8
src
builtins
builtins-collections-gen.cc
// Copyright 2017 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/builtins/builtins-constructor-gen.h" #include "src/builtins/builtins-iterator-gen.h" #include "src/builtins/builtins-utils-gen.h" #include "src/code-stub-assembler.h" #include "src/heap/factory-inl.h" #include "src/objects/hash-table-inl.h" #include "src/objects/js-collection.h" namespace v8 { namespace internal { using compiler::Node; template
using TNode = compiler::TNode
; template
using TVariable = compiler::TypedCodeAssemblerVariable
; class BaseCollectionsAssembler : public CodeStubAssembler { public: explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state) : CodeStubAssembler(state) {} virtual ~BaseCollectionsAssembler() {} protected: enum Variant { kMap, kSet, kWeakMap, kWeakSet }; // Adds an entry to a collection. For Maps, properly handles extracting the // key and value from the entry (see LoadKeyValue()). void AddConstructorEntry(Variant variant, TNode
context, TNode
collection, TNode
add_function, TNode
key_value, Label* if_may_have_side_effects = nullptr, Label* if_exception = nullptr, TVariable
* var_exception = nullptr); // Adds constructor entries to a collection. Choosing a fast path when // possible. void AddConstructorEntries(Variant variant, TNode
context, TNode
native_context, TNode
collection, TNode
initial_entries); // Fast path for adding constructor entries. Assumes the entries are a fast // JS array (see CodeStubAssembler::BranchIfFastJSArray()). void AddConstructorEntriesFromFastJSArray(Variant variant, TNode
context, TNode
native_context, TNode
collection, TNode
fast_jsarray, Label* if_may_have_side_effects); // Adds constructor entries to a collection using the iterator protocol. void AddConstructorEntriesFromIterable(Variant variant, TNode
context, TNode
native_context, TNode
collection, TNode
iterable); // Constructs a collection instance. Choosing a fast path when possible. TNode
AllocateJSCollection(TNode
context, TNode
constructor, TNode
new_target); // Fast path for constructing a collection instance if the constructor // function has not been modified. TNode
AllocateJSCollectionFast(TNode
constructor); // Fallback for constructing a collection instance if the constructor function // has been modified. TNode
AllocateJSCollectionSlow(TNode
context, TNode
constructor, TNode
new_target); // Allocates the backing store for a collection. virtual TNode
AllocateTable(Variant variant, TNode
context, TNode
at_least_space_for) = 0; // Main entry point for a collection constructor builtin. void GenerateConstructor(Variant variant, Handle
constructor_function_name, TNode
new_target, TNode
argc, TNode
context); // Retrieves the collection function that adds an entry. `set` for Maps and // `add` for Sets. TNode
GetAddFunction(Variant variant, TNode
context, TNode
collection); // Retrieves the collection constructor function. TNode
GetConstructor(Variant variant, TNode
native_context); // Retrieves the initial collection function that adds an entry. Should only // be called when it is certain that a collection prototype's map hasn't been // changed. TNode
GetInitialAddFunction(Variant variant, TNode
native_context); // Retrieves the offset to access the backing table from the collection. int GetTableOffset(Variant variant); // Estimates the number of entries the collection will have after adding the // entries passed in the constructor. AllocateTable() can use this to avoid // the time of growing/rehashing when adding the constructor entries. TNode
EstimatedInitialSize(TNode
initial_entries, TNode
is_fast_jsarray); void GotoIfNotJSReceiver(Node* const obj, Label* if_not_receiver); // Determines whether the collection's prototype has been modified. TNode
HasInitialCollectionPrototype(Variant variant, TNode
native_context, TNode
collection); // Loads an element from a fixed array. If the element is the hole, returns // `undefined`. TNode
LoadAndNormalizeFixedArrayElement(TNode
elements, TNode
index); // Loads an element from a fixed double array. If the element is the hole, // returns `undefined`. TNode
LoadAndNormalizeFixedDoubleArrayElement( TNode
elements, TNode
index); // Loads key and value variables with the first and second elements of an // array. If the array lacks 2 elements, undefined is used. void LoadKeyValue(TNode
context, TNode
maybe_array, TVariable
* key, TVariable
* value, Label* if_may_have_side_effects = nullptr, Label* if_exception = nullptr, TVariable
* var_exception = nullptr); }; void BaseCollectionsAssembler::AddConstructorEntry( Variant variant, TNode
context, TNode
collection, TNode
add_function, TNode
key_value, Label* if_may_have_side_effects, Label* if_exception, TVariable
* var_exception) { CSA_ASSERT(this, Word32BinaryNot(IsTheHole(key_value))); if (variant == kMap || variant == kWeakMap) { TVARIABLE(Object, key); TVARIABLE(Object, value); LoadKeyValue(context, key_value, &key, &value, if_may_have_side_effects, if_exception, var_exception); Node* key_n = key.value(); Node* value_n = value.value(); Node* ret = CallJS(CodeFactory::Call(isolate()), context, add_function, collection, key_n, value_n); GotoIfException(ret, if_exception, var_exception); } else { DCHECK(variant == kSet || variant == kWeakSet); Node* ret = CallJS(CodeFactory::Call(isolate()), context, add_function, collection, key_value); GotoIfException(ret, if_exception, var_exception); } } void BaseCollectionsAssembler::AddConstructorEntries( Variant variant, TNode
context, TNode
native_context, TNode
collection, TNode
initial_entries) { TVARIABLE(BoolT, use_fast_loop, IsFastJSArrayWithNoCustomIteration(initial_entries, context)); TNode
at_least_space_for = EstimatedInitialSize(initial_entries, use_fast_loop.value()); Label allocate_table(this, &use_fast_loop), exit(this), fast_loop(this), slow_loop(this, Label::kDeferred); Goto(&allocate_table); BIND(&allocate_table); { TNode
table = AllocateTable(variant, context, at_least_space_for); StoreObjectField(collection, GetTableOffset(variant), table); GotoIf(IsNullOrUndefined(initial_entries), &exit); GotoIfNot( HasInitialCollectionPrototype(variant, native_context, collection), &slow_loop); Branch(use_fast_loop.value(), &fast_loop, &slow_loop); } BIND(&fast_loop); { TNode
initial_entries_jsarray = UncheckedCast
(initial_entries); #if DEBUG CSA_ASSERT(this, IsFastJSArrayWithNoCustomIteration(initial_entries_jsarray, context)); TNode
original_initial_entries_map = LoadMap(initial_entries_jsarray); #endif Label if_may_have_side_effects(this, Label::kDeferred); AddConstructorEntriesFromFastJSArray(variant, context, native_context, collection, initial_entries_jsarray, &if_may_have_side_effects); Goto(&exit); if (variant == kMap || variant == kWeakMap) { BIND(&if_may_have_side_effects); #if DEBUG CSA_ASSERT(this, HasInitialCollectionPrototype(variant, native_context, collection)); CSA_ASSERT(this, WordEqual(original_initial_entries_map, LoadMap(initial_entries_jsarray))); #endif use_fast_loop = Int32FalseConstant(); Goto(&allocate_table); } } BIND(&slow_loop); { AddConstructorEntriesFromIterable(variant, context, native_context, collection, initial_entries); Goto(&exit); } BIND(&exit); } void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray( Variant variant, TNode
context, TNode
native_context, TNode
collection, TNode
fast_jsarray, Label* if_may_have_side_effects) { TNode
elements = LoadElements(fast_jsarray); TNode
elements_kind = LoadElementsKind(fast_jsarray); TNode
add_func = GetInitialAddFunction(variant, native_context); CSA_ASSERT( this, WordEqual(GetAddFunction(variant, native_context, collection), add_func)); CSA_ASSERT(this, IsFastJSArrayWithNoCustomIteration(fast_jsarray, context)); TNode
length = SmiUntag(LoadFastJSArrayLength(fast_jsarray)); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(length, IntPtrConstant(0))); CSA_ASSERT( this, HasInitialCollectionPrototype(variant, native_context, collection)); #if DEBUG TNode
original_collection_map = LoadMap(CAST(collection)); TNode
original_fast_js_array_map = LoadMap(fast_jsarray); #endif Label exit(this), if_doubles(this), if_smiorobjects(this); GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &exit); Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects, &if_doubles); BIND(&if_smiorobjects); { auto set_entry = [&](Node* index) { TNode
element = LoadAndNormalizeFixedArrayElement( CAST(elements), UncheckedCast
(index)); AddConstructorEntry(variant, context, collection, add_func, element, if_may_have_side_effects); }; // Instead of using the slower iteration protocol to iterate over the // elements, a fast loop is used. This assumes that adding an element // to the collection does not call user code that could mutate the elements // or collection. BuildFastLoop(IntPtrConstant(0), length, set_entry, 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost); Goto(&exit); } BIND(&if_doubles); { // A Map constructor requires entries to be arrays (ex. [key, value]), // so a FixedDoubleArray can never succeed. if (variant == kMap || variant == kWeakMap) { CSA_ASSERT(this, IntPtrGreaterThan(length, IntPtrConstant(0))); TNode
element = LoadAndNormalizeFixedDoubleArrayElement(elements, IntPtrConstant(0)); ThrowTypeError(context, MessageTemplate::kIteratorValueNotAnObject, element); } else { DCHECK(variant == kSet || variant == kWeakSet); auto set_entry = [&](Node* index) { TNode
entry = LoadAndNormalizeFixedDoubleArrayElement( elements, UncheckedCast
(index)); AddConstructorEntry(variant, context, collection, add_func, entry); }; BuildFastLoop(IntPtrConstant(0), length, set_entry, 1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost); Goto(&exit); } } BIND(&exit); #if DEBUG CSA_ASSERT(this, WordEqual(original_collection_map, LoadMap(CAST(collection)))); CSA_ASSERT(this, WordEqual(original_fast_js_array_map, LoadMap(fast_jsarray))); #endif } void BaseCollectionsAssembler::AddConstructorEntriesFromIterable( Variant variant, TNode
context, TNode
native_context, TNode
collection, TNode
iterable) { Label exit(this), loop(this), if_exception(this, Label::kDeferred); CSA_ASSERT(this, Word32BinaryNot(IsNullOrUndefined(iterable))); TNode
add_func = GetAddFunction(variant, context, collection); IteratorBuiltinsAssembler iterator_assembler(this->state()); IteratorRecord iterator = iterator_assembler.GetIterator(context, iterable); CSA_ASSERT(this, Word32BinaryNot(IsUndefined(iterator.object))); TNode
fast_iterator_result_map = LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); TVARIABLE(Object, var_exception); Goto(&loop); BIND(&loop); { TNode
next = CAST(iterator_assembler.IteratorStep( context, iterator, &exit, fast_iterator_result_map)); TNode
next_value = CAST(iterator_assembler.IteratorValue( context, next, fast_iterator_result_map)); AddConstructorEntry(variant, context, collection, add_func, next_value, nullptr, &if_exception, &var_exception); Goto(&loop); } BIND(&if_exception); { iterator_assembler.IteratorCloseOnException(context, iterator, &var_exception); } BIND(&exit); } TNode
BaseCollectionsAssembler::AllocateJSCollection( TNode
context, TNode
constructor, TNode
new_target) { TNode
is_target_unmodified = WordEqual(constructor, new_target); return Select
(is_target_unmodified, [=] { return AllocateJSCollectionFast(constructor); }, [=] { return AllocateJSCollectionSlow(context, constructor, new_target); }); } TNode
BaseCollectionsAssembler::AllocateJSCollectionFast( TNode
constructor) { CSA_ASSERT(this, IsConstructorMap(LoadMap(constructor))); TNode
initial_map = LoadObjectField(constructor, JSFunction::kPrototypeOrInitialMapOffset); return CAST(AllocateJSObjectFromMap(initial_map)); } TNode
BaseCollectionsAssembler::AllocateJSCollectionSlow( TNode
context, TNode
constructor, TNode
new_target) { ConstructorBuiltinsAssembler constructor_assembler(this->state()); return CAST(constructor_assembler.EmitFastNewObject(context, constructor, new_target)); } void BaseCollectionsAssembler::GenerateConstructor( Variant variant, Handle
constructor_function_name, TNode
new_target, TNode
argc, TNode
context) { const int kIterableArg = 0; CodeStubArguments args(this, argc); TNode
iterable = args.GetOptionalArgumentValue(kIterableArg); Label if_undefined(this, Label::kDeferred); GotoIf(IsUndefined(new_target), &if_undefined); TNode
native_context = LoadNativeContext(context); TNode
collection = AllocateJSCollection( context, GetConstructor(variant, native_context), new_target); AddConstructorEntries(variant, context, native_context, collection, iterable); Return(collection); BIND(&if_undefined); ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, HeapConstant(constructor_function_name)); } TNode
BaseCollectionsAssembler::GetAddFunction( Variant variant, TNode
context, TNode
collection) { Handle
add_func_name = (variant == kMap || variant == kWeakMap) ? isolate()->factory()->set_string() : isolate()->factory()->add_string(); TNode
add_func = GetProperty(context, collection, add_func_name); Label exit(this), if_notcallable(this, Label::kDeferred); GotoIf(TaggedIsSmi(add_func), &if_notcallable); GotoIfNot(IsCallable(CAST(add_func)), &if_notcallable); Goto(&exit); BIND(&if_notcallable); ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, add_func, HeapConstant(add_func_name), collection); BIND(&exit); return add_func; } TNode
BaseCollectionsAssembler::GetConstructor( Variant variant, TNode
native_context) { int index; switch (variant) { case kMap: index = Context::JS_MAP_FUN_INDEX; break; case kSet: index = Context::JS_SET_FUN_INDEX; break; case kWeakMap: index = Context::JS_WEAK_MAP_FUN_INDEX; break; case kWeakSet: index = Context::JS_WEAK_SET_FUN_INDEX; break; } return CAST(LoadContextElement(native_context, index)); } TNode
BaseCollectionsAssembler::GetInitialAddFunction( Variant variant, TNode
native_context) { int index; switch (variant) { case kMap: index = Context::MAP_SET_INDEX; break; case kSet: index = Context::SET_ADD_INDEX; break; case kWeakMap: index = Context::WEAKMAP_SET_INDEX; break; case kWeakSet: index = Context::WEAKSET_ADD_INDEX; break; } return CAST(LoadContextElement(native_context, index)); } int BaseCollectionsAssembler::GetTableOffset(Variant variant) { switch (variant) { case kMap: return JSMap::kTableOffset; case kSet: return JSSet::kTableOffset; case kWeakMap: return JSWeakMap::kTableOffset; case kWeakSet: return JSWeakSet::kTableOffset; } UNREACHABLE(); } TNode
BaseCollectionsAssembler::EstimatedInitialSize( TNode
initial_entries, TNode
is_fast_jsarray) { return Select
( is_fast_jsarray, [=] { return SmiUntag(LoadFastJSArrayLength(CAST(initial_entries))); }, [=] { return IntPtrConstant(0); }); } void BaseCollectionsAssembler::GotoIfNotJSReceiver(Node* const obj, Label* if_not_receiver) { GotoIf(TaggedIsSmi(obj), if_not_receiver); GotoIfNot(IsJSReceiver(obj), if_not_receiver); } TNode
BaseCollectionsAssembler::HasInitialCollectionPrototype( Variant variant, TNode
native_context, TNode
collection) { int initial_prototype_index; switch (variant) { case kMap: initial_prototype_index = Context::INITIAL_MAP_PROTOTYPE_MAP_INDEX; break; case kSet: initial_prototype_index = Context::INITIAL_SET_PROTOTYPE_MAP_INDEX; break; case kWeakMap: initial_prototype_index = Context::INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX; break; case kWeakSet: initial_prototype_index = Context::INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX; break; } TNode
initial_prototype_map = CAST(LoadContextElement(native_context, initial_prototype_index)); TNode
collection_proto_map = LoadMap(LoadMapPrototype(LoadMap(CAST(collection)))); return WordEqual(collection_proto_map, initial_prototype_map); } TNode
BaseCollectionsAssembler::LoadAndNormalizeFixedArrayElement( TNode
elements, TNode
index) { TNode
element = LoadFixedArrayElement(elements, index); return Select
(IsTheHole(element), [=] { return UndefinedConstant(); }, [=] { return element; }); } TNode
BaseCollectionsAssembler::LoadAndNormalizeFixedDoubleArrayElement( TNode
elements, TNode
index) { TVARIABLE(Object, entry); Label if_hole(this, Label::kDeferred), next(this); TNode
element = LoadFixedDoubleArrayElement(CAST(elements), index, MachineType::Float64(), 0, INTPTR_PARAMETERS, &if_hole); { // not hole entry = AllocateHeapNumberWithValue(element); Goto(&next); } BIND(&if_hole); { entry = UndefinedConstant(); Goto(&next); } BIND(&next); return entry.value(); } void BaseCollectionsAssembler::LoadKeyValue( TNode
context, TNode
maybe_array, TVariable
* key, TVariable
* value, Label* if_may_have_side_effects, Label* if_exception, TVariable
* var_exception) { CSA_ASSERT(this, Word32BinaryNot(IsTheHole(maybe_array))); Label exit(this), if_fast(this), if_slow(this, Label::kDeferred); BranchIfFastJSArray(maybe_array, context, &if_fast, &if_slow); BIND(&if_fast); { TNode
array = CAST(maybe_array); TNode
length = LoadFastJSArrayLength(array); TNode
elements = LoadElements(array); TNode
elements_kind = LoadElementsKind(array); Label if_smiorobjects(this), if_doubles(this); Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects, &if_doubles); BIND(&if_smiorobjects); { Label if_one(this), if_two(this); GotoIf(SmiGreaterThan(length, SmiConstant(1)), &if_two); GotoIf(SmiEqual(length, SmiConstant(1)), &if_one); { // empty array *key = UndefinedConstant(); *value = UndefinedConstant(); Goto(&exit); } BIND(&if_one); { *key = LoadAndNormalizeFixedArrayElement(CAST(elements), IntPtrConstant(0)); *value = UndefinedConstant(); Goto(&exit); } BIND(&if_two); { TNode
elements_fixed_array = CAST(elements); *key = LoadAndNormalizeFixedArrayElement(elements_fixed_array, IntPtrConstant(0)); *value = LoadAndNormalizeFixedArrayElement(elements_fixed_array, IntPtrConstant(1)); Goto(&exit); } } BIND(&if_doubles); { Label if_one(this), if_two(this); GotoIf(SmiGreaterThan(length, SmiConstant(1)), &if_two); GotoIf(SmiEqual(length, SmiConstant(1)), &if_one); { // empty array *key = UndefinedConstant(); *value = UndefinedConstant(); Goto(&exit); } BIND(&if_one); { *key = LoadAndNormalizeFixedDoubleArrayElement(elements, IntPtrConstant(0)); *value = UndefinedConstant(); Goto(&exit); } BIND(&if_two); { *key = LoadAndNormalizeFixedDoubleArrayElement(elements, IntPtrConstant(0)); *value = LoadAndNormalizeFixedDoubleArrayElement(elements, IntPtrConstant(1)); Goto(&exit); } } } BIND(&if_slow); { Label if_notobject(this, Label::kDeferred); GotoIfNotJSReceiver(maybe_array, &if_notobject); if (if_may_have_side_effects != nullptr) { // If the element is not a fast array, we cannot guarantee accessing the // key and value won't execute user code that will break fast path // assumptions. Goto(if_may_have_side_effects); } else { *key = UncheckedCast
(GetProperty( context, maybe_array, isolate()->factory()->zero_string())); GotoIfException(key->value(), if_exception, var_exception); *value = UncheckedCast
(GetProperty( context, maybe_array, isolate()->factory()->one_string())); GotoIfException(value->value(), if_exception, var_exception); Goto(&exit); } BIND(&if_notobject); { Node* ret = CallRuntime( Runtime::kThrowTypeError, context, SmiConstant(MessageTemplate::kIteratorValueNotAnObject), maybe_array); GotoIfException(ret, if_exception, var_exception); Unreachable(); } } BIND(&exit); } class CollectionsBuiltinsAssembler : public BaseCollectionsAssembler { public: explicit CollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state) : BaseCollectionsAssembler(state) {} protected: template
Node* AllocateJSCollectionIterator(Node* context, int map_index, Node* collection); TNode
AllocateTable(Variant variant, TNode
context, TNode
at_least_space_for); Node* GetHash(Node* const key); Node* CallGetHashRaw(Node* const key); Node* CallGetOrCreateHashRaw(Node* const key); // Transitions the iterator to the non obsolete backing store. // This is a NOP if the [table] is not obsolete. typedef std::function
UpdateInTransition; template
std::pair
, TNode
> Transition( TNode
const table, TNode
const index, UpdateInTransition const& update_in_transition); template
std::pair
, TNode
> TransitionAndUpdate( TNode
const iterator); template
std::tuple
, TNode
, TNode
> NextSkipHoles( TNode
table, TNode
index, Label* if_end); // Specialization for Smi. // The {result} variable will contain the entry index if the key was found, // or the hash code otherwise. template
void FindOrderedHashTableEntryForSmiKey(Node* table, Node* key_tagged, Variable* result, Label* entry_found, Label* not_found); void SameValueZeroSmi(Node* key_smi, Node* candidate_key, Label* if_same, Label* if_not_same); // Specialization for heap numbers. // The {result} variable will contain the entry index if the key was found, // or the hash code otherwise. void SameValueZeroHeapNumber(Node* key_string, Node* candidate_key, Label* if_same, Label* if_not_same); template
void FindOrderedHashTableEntryForHeapNumberKey(Node* context, Node* table, Node* key_heap_number, Variable* result, Label* entry_found, Label* not_found); // Specialization for bigints. // The {result} variable will contain the entry index if the key was found, // or the hash code otherwise. void SameValueZeroBigInt(Node* key, Node* candidate_key, Label* if_same, Label* if_not_same); template
void FindOrderedHashTableEntryForBigIntKey(Node* context, Node* table, Node* key, Variable* result, Label* entry_found, Label* not_found); // Specialization for string. // The {result} variable will contain the entry index if the key was found, // or the hash code otherwise. template
void FindOrderedHashTableEntryForStringKey(Node* context, Node* table, Node* key_tagged, Variable* result, Label* entry_found, Label* not_found); Node* ComputeIntegerHashForString(Node* context, Node* string_key); void SameValueZeroString(Node* context, Node* key_string, Node* candidate_key, Label* if_same, Label* if_not_same); // Specialization for non-strings, non-numbers. For those we only need // reference equality to compare the keys. // The {result} variable will contain the entry index if the key was found, // or the hash code otherwise. If the hash-code has not been computed, it // should be Smi -1. template
void FindOrderedHashTableEntryForOtherKey(Node* context, Node* table, Node* key, Variable* result, Label* entry_found, Label* not_found); template
void TryLookupOrderedHashTableIndex(Node* const table, Node* const key, Node* const context, Variable* result, Label* if_entry_found, Label* if_not_found); Node* NormalizeNumberKey(Node* key); void StoreOrderedHashMapNewEntry(TNode
const table, Node* const key, Node* const value, Node* const hash, Node* const number_of_buckets, Node* const occupancy); void StoreOrderedHashSetNewEntry(TNode
const table, Node* const key, Node* const hash, Node* const number_of_buckets, Node* const occupancy); }; template
Node* CollectionsBuiltinsAssembler::AllocateJSCollectionIterator( Node* context, int map_index, Node* collection) { Node* const table = LoadObjectField(collection, JSCollection::kTableOffset); Node* const native_context = LoadNativeContext(context); Node* const iterator_map = LoadContextElement(native_context, map_index); Node* const iterator = AllocateInNewSpace(IteratorType::kSize); StoreMapNoWriteBarrier(iterator, iterator_map); StoreObjectFieldRoot(iterator, IteratorType::kPropertiesOrHashOffset, Heap::kEmptyFixedArrayRootIndex); StoreObjectFieldRoot(iterator, IteratorType::kElementsOffset, Heap::kEmptyFixedArrayRootIndex); StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kTableOffset, table); StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset, SmiConstant(0)); return iterator; } TNode
CollectionsBuiltinsAssembler::AllocateTable( Variant variant, TNode
context, TNode
at_least_space_for) { return CAST((variant == kMap || variant == kWeakMap) ? AllocateOrderedHashTable
() : AllocateOrderedHashTable
()); } TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) { TNode
new_target = CAST(Parameter(Descriptor::kJSNewTarget)); TNode
argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); TNode
context = CAST(Parameter(Descriptor::kContext)); GenerateConstructor(kMap, isolate()->factory()->Map_string(), new_target, argc, context); } TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) { TNode
new_target = CAST(Parameter(Descriptor::kJSNewTarget)); TNode
argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); TNode
context = CAST(Parameter(Descriptor::kContext)); GenerateConstructor(kSet, isolate()->factory()->Set_string(), new_target, argc, context); } Node* CollectionsBuiltinsAssembler::CallGetOrCreateHashRaw(Node* const key) { Node* const function_addr = ExternalConstant(ExternalReference::get_or_create_hash_raw(isolate())); Node* const isolate_ptr = ExternalConstant(ExternalReference::isolate_address(isolate())); MachineType type_ptr = MachineType::Pointer(); MachineType type_tagged = MachineType::AnyTagged(); Node* const result = CallCFunction2(type_tagged, type_ptr, type_tagged, function_addr, isolate_ptr, key); return result; } Node* CollectionsBuiltinsAssembler::CallGetHashRaw(Node* const key) { Node* const function_addr = ExternalConstant(ExternalReference::orderedhashmap_gethash_raw()); Node* const isolate_ptr = ExternalConstant(ExternalReference::isolate_address(isolate())); MachineType type_ptr = MachineType::Pointer(); MachineType type_tagged = MachineType::AnyTagged(); Node* const result = CallCFunction2(type_tagged, type_ptr, type_tagged, function_addr, isolate_ptr, key); return SmiUntag(result); } Node* CollectionsBuiltinsAssembler::GetHash(Node* const key) { VARIABLE(var_hash, MachineType::PointerRepresentation()); Label if_receiver(this), if_other(this), done(this); Branch(IsJSReceiver(key), &if_receiver, &if_other); BIND(&if_receiver); { var_hash.Bind(LoadJSReceiverIdentityHash(key)); Goto(&done); } BIND(&if_other); { var_hash.Bind(CallGetHashRaw(key)); Goto(&done); } BIND(&done); return var_hash.value(); } void CollectionsBuiltinsAssembler::SameValueZeroSmi(Node* key_smi, Node* candidate_key, Label* if_same, Label* if_not_same) { // If the key is the same, we are done. GotoIf(WordEqual(candidate_key, key_smi), if_same); // If the candidate key is smi, then it must be different (because // we already checked for equality above). GotoIf(TaggedIsSmi(candidate_key), if_not_same); // If the candidate key is not smi, we still have to check if it is a // heap number with the same value. GotoIfNot(IsHeapNumber(candidate_key), if_not_same); Node* const candidate_key_number = LoadHeapNumberValue(candidate_key); Node* const key_number = SmiToFloat64(key_smi); GotoIf(Float64Equal(candidate_key_number, key_number), if_same); Goto(if_not_same); } template
void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForSmiKey( Node* table, Node* smi_key, Variable* result, Label* entry_found, Label* not_found) { Node* const key_untagged = SmiUntag(smi_key); Node* const hash = ChangeInt32ToIntPtr(ComputeIntegerHash(key_untagged, Int32Constant(0))); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0))); result->Bind(hash); FindOrderedHashTableEntry
( table, hash, [&](Node* other_key, Label* if_same, Label* if_not_same) { SameValueZeroSmi(smi_key, other_key, if_same, if_not_same); }, result, entry_found, not_found); } template
void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForStringKey( Node* context, Node* table, Node* key_tagged, Variable* result, Label* entry_found, Label* not_found) { Node* const hash = ComputeIntegerHashForString(context, key_tagged); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0))); result->Bind(hash); FindOrderedHashTableEntry
( table, hash, [&](Node* other_key, Label* if_same, Label* if_not_same) { SameValueZeroString(context, key_tagged, other_key, if_same, if_not_same); }, result, entry_found, not_found); } template
void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForHeapNumberKey( Node* context, Node* table, Node* key_heap_number, Variable* result, Label* entry_found, Label* not_found) { Node* hash = CallGetHashRaw(key_heap_number); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0))); result->Bind(hash); Node* const key_float = LoadHeapNumberValue(key_heap_number); FindOrderedHashTableEntry
( table, hash, [&](Node* other_key, Label* if_same, Label* if_not_same) { SameValueZeroHeapNumber(key_float, other_key, if_same, if_not_same); }, result, entry_found, not_found); } template
void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForBigIntKey( Node* context, Node* table, Node* key, Variable* result, Label* entry_found, Label* not_found) { Node* hash = CallGetHashRaw(key); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0))); result->Bind(hash); FindOrderedHashTableEntry
( table, hash, [&](Node* other_key, Label* if_same, Label* if_not_same) { SameValueZeroBigInt(key, other_key, if_same, if_not_same); }, result, entry_found, not_found); } template
void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForOtherKey( Node* context, Node* table, Node* key, Variable* result, Label* entry_found, Label* not_found) { Node* hash = GetHash(key); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0))); result->Bind(hash); FindOrderedHashTableEntry
( table, hash, [&](Node* other_key, Label* if_same, Label* if_not_same) { Branch(WordEqual(key, other_key), if_same, if_not_same); }, result, entry_found, not_found); } Node* CollectionsBuiltinsAssembler::ComputeIntegerHashForString( Node* context, Node* string_key) { VARIABLE(var_result, MachineType::PointerRepresentation()); Label hash_not_computed(this), done(this, &var_result); Node* hash = ChangeInt32ToIntPtr(LoadNameHash(string_key, &hash_not_computed)); var_result.Bind(hash); Goto(&done); BIND(&hash_not_computed); var_result.Bind(CallGetHashRaw(string_key)); Goto(&done); BIND(&done); return var_result.value(); } void CollectionsBuiltinsAssembler::SameValueZeroString(Node* context, Node* key_string, Node* candidate_key, Label* if_same, Label* if_not_same) { // If the candidate is not a string, the keys are not equal. GotoIf(TaggedIsSmi(candidate_key), if_not_same); GotoIfNot(IsString(candidate_key), if_not_same); Branch(WordEqual(CallBuiltin(Builtins::kStringEqual, context, key_string, candidate_key), TrueConstant()), if_same, if_not_same); } void CollectionsBuiltinsAssembler::SameValueZeroBigInt(Node* key, Node* candidate_key, Label* if_same, Label* if_not_same) { CSA_ASSERT(this, IsBigInt(key)); GotoIf(TaggedIsSmi(candidate_key), if_not_same); GotoIfNot(IsBigInt(candidate_key), if_not_same); Branch(WordEqual(CallRuntime(Runtime::kBigIntEqualToBigInt, NoContextConstant(), key, candidate_key), TrueConstant()), if_same, if_not_same); } void CollectionsBuiltinsAssembler::SameValueZeroHeapNumber(Node* key_float, Node* candidate_key, Label* if_same, Label* if_not_same) { Label if_smi(this), if_keyisnan(this); GotoIf(TaggedIsSmi(candidate_key), &if_smi); GotoIfNot(IsHeapNumber(candidate_key), if_not_same); { // {candidate_key} is a heap number. Node* const candidate_float = LoadHeapNumberValue(candidate_key); GotoIf(Float64Equal(key_float, candidate_float), if_same); // SameValueZero needs to treat NaNs as equal. First check if {key_float} // is NaN. BranchIfFloat64IsNaN(key_float, &if_keyisnan, if_not_same); BIND(&if_keyisnan); { // Return true iff {candidate_key} is NaN. Branch(Float64Equal(candidate_float, candidate_float), if_not_same, if_same); } } BIND(&if_smi); { Node* const candidate_float = SmiToFloat64(candidate_key); Branch(Float64Equal(key_float, candidate_float), if_same, if_not_same); } } TF_BUILTIN(OrderedHashTableHealIndex, CollectionsBuiltinsAssembler) { TNode
table = CAST(Parameter(Descriptor::kTable)); TNode
index = CAST(Parameter(Descriptor::kIndex)); Label return_index(this), return_zero(this); // Check if we need to update the {index}. GotoIfNot(SmiLessThan(SmiConstant(0), index), &return_zero); // Check if the {table} was cleared. Node* number_of_deleted_elements = LoadAndUntagObjectField( table, OrderedHashTableBase::kNumberOfDeletedElementsOffset); GotoIf(WordEqual(number_of_deleted_elements, IntPtrConstant(OrderedHashTableBase::kClearedTableSentinel)), &return_zero); VARIABLE(var_i, MachineType::PointerRepresentation(), IntPtrConstant(0)); VARIABLE(var_index, MachineRepresentation::kTagged, index); Label loop(this, {&var_i, &var_index}); Goto(&loop); BIND(&loop); { Node* i = var_i.value(); GotoIfNot(IntPtrLessThan(i, number_of_deleted_elements), &return_index); TNode
removed_index = CAST(LoadFixedArrayElement( CAST(table), i, OrderedHashTableBase::kRemovedHolesIndex * kPointerSize)); GotoIf(SmiGreaterThanOrEqual(removed_index, index), &return_index); Decrement(&var_index, 1, SMI_PARAMETERS); Increment(&var_i); Goto(&loop); } BIND(&return_index); Return(var_index.value()); BIND(&return_zero); Return(SmiConstant(0)); } template
std::pair
, TNode
> CollectionsBuiltinsAssembler::Transition( TNode
const table, TNode
const index, UpdateInTransition const& update_in_transition) { TVARIABLE(IntPtrT, var_index, index); TVARIABLE(TableType, var_table, table); Label if_done(this), if_transition(this, Label::kDeferred); Branch(TaggedIsSmi( LoadObjectField(var_table.value(), TableType::kNextTableOffset)), &if_done, &if_transition); BIND(&if_transition); { Label loop(this, {&var_table, &var_index}), done_loop(this); Goto(&loop); BIND(&loop); { TNode
table = var_table.value(); TNode
index = var_index.value(); TNode
next_table = LoadObjectField(table, TableType::kNextTableOffset); GotoIf(TaggedIsSmi(next_table), &done_loop); var_table = CAST(next_table); var_index = SmiUntag( CAST(CallBuiltin(Builtins::kOrderedHashTableHealIndex, NoContextConstant(), table, SmiTag(index)))); Goto(&loop); } BIND(&done_loop); // Update with the new {table} and {index}. update_in_transition(var_table.value(), var_index.value()); Goto(&if_done); } BIND(&if_done); return {var_table.value(), var_index.value()}; } template
std::pair
, TNode
> CollectionsBuiltinsAssembler::TransitionAndUpdate( TNode
const iterator) { return Transition
( CAST(LoadObjectField(iterator, IteratorType::kTableOffset)), LoadAndUntagObjectField(iterator, IteratorType::kIndexOffset), [this, iterator](Node* const table, Node* const index) { // Update the {iterator} with the new state. StoreObjectField(iterator, IteratorType::kTableOffset, table); StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset, SmiTag(index)); }); } template
std::tuple
, TNode
, TNode
> CollectionsBuiltinsAssembler::NextSkipHoles(TNode
table, TNode
index, Label* if_end) { // Compute the used capacity for the {table}. TNode
number_of_buckets = LoadAndUntagObjectField(table, TableType::kNumberOfBucketsOffset); TNode
number_of_elements = LoadAndUntagObjectField(table, TableType::kNumberOfElementsOffset); TNode
number_of_deleted_elements = LoadAndUntagObjectField(table, TableType::kNumberOfDeletedElementsOffset); TNode
used_capacity = IntPtrAdd(number_of_elements, number_of_deleted_elements); TNode
entry_key; TNode
entry_start_position; TVARIABLE(IntPtrT, var_index, index); Label loop(this, &var_index), done_loop(this); Goto(&loop); BIND(&loop); { GotoIfNot(IntPtrLessThan(var_index.value(), used_capacity), if_end); entry_start_position = IntPtrAdd( IntPtrMul(var_index.value(), IntPtrConstant(TableType::kEntrySize)), number_of_buckets); entry_key = LoadFixedArrayElement(table, entry_start_position, TableType::kHashTableStartIndex * kPointerSize); Increment(&var_index); Branch(IsTheHole(entry_key), &loop, &done_loop); } BIND(&done_loop); return std::tuple
, TNode
, TNode
>{ entry_key, entry_start_position, var_index.value()}; } TF_BUILTIN(MapPrototypeGet, CollectionsBuiltinsAssembler) { Node* const receiver = Parameter(Descriptor::kReceiver); Node* const key = Parameter(Descriptor::kKey); Node* const context = Parameter(Descriptor::kContext); ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.get"); Node* const table = LoadObjectField(receiver, JSMap::kTableOffset); TNode
index = CAST( CallBuiltin(Builtins::kFindOrderedHashMapEntry, context, table, key)); Label if_found(this), if_not_found(this); Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found, &if_not_found); BIND(&if_found); Return(LoadFixedArrayElement( CAST(table), SmiUntag(index), (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset) * kPointerSize)); BIND(&if_not_found); Return(UndefinedConstant()); } TF_BUILTIN(MapPrototypeHas, CollectionsBuiltinsAssembler) { Node* const receiver = Parameter(Descriptor::kReceiver); Node* const key = Parameter(Descriptor::kKey); Node* const context = Parameter(Descriptor::kContext); ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.has"); Node* const table = LoadObjectField(receiver, JSMap::kTableOffset); TNode
index = CAST( CallBuiltin(Builtins::kFindOrderedHashMapEntry, context, table, key)); Label if_found(this), if_not_found(this); Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found, &if_not_found); BIND(&if_found); Return(TrueConstant()); BIND(&if_not_found); Return(FalseConstant()); } Node* CollectionsBuiltinsAssembler::NormalizeNumberKey(Node* const key) { VARIABLE(result, MachineRepresentation::kTagged, key); Label done(this); GotoIf(TaggedIsSmi(key), &done); GotoIfNot(IsHeapNumber(key), &done); Node* const number = LoadHeapNumberValue(key); GotoIfNot(Float64Equal(number, Float64Constant(0.0)), &done); // We know the value is zero, so we take the key to be Smi 0. // Another option would be to normalize to Smi here. result.Bind(SmiConstant(0)); Goto(&done); BIND(&done); return result.value(); } TF_BUILTIN(MapPrototypeSet, CollectionsBuiltinsAssembler) { Node* const receiver = Parameter(Descriptor::kReceiver); Node* key = Parameter(Descriptor::kKey); Node* const value = Parameter(Descriptor::kValue); Node* const context = Parameter(Descriptor::kContext); ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.set"); key = NormalizeNumberKey(key); TNode
const table = CAST(LoadObjectField(receiver, JSMap::kTableOffset)); VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(), IntPtrConstant(0)); Label entry_found(this), not_found(this); TryLookupOrderedHashTableIndex
(table, key, context, &entry_start_position_or_hash, &entry_found, ¬_found); BIND(&entry_found); // If we found the entry, we just store the value there. StoreFixedArrayElement(table, entry_start_position_or_hash.value(), value, UPDATE_WRITE_BARRIER, kPointerSize * (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset)); Return(receiver); Label no_hash(this), add_entry(this), store_new_entry(this); BIND(¬_found); { // If we have a hash code, we can start adding the new entry. GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(), IntPtrConstant(0)), &add_entry); // Otherwise, go to runtime to compute the hash code. entry_start_position_or_hash.Bind(SmiUntag(CallGetOrCreateHashRaw(key))); Goto(&add_entry); } BIND(&add_entry); VARIABLE(number_of_buckets, MachineType::PointerRepresentation()); VARIABLE(occupancy, MachineType::PointerRepresentation()); TVARIABLE(OrderedHashMap, table_var, table); { // Check we have enough space for the entry. number_of_buckets.Bind(SmiUntag(CAST( LoadFixedArrayElement(table, OrderedHashMap::kNumberOfBucketsIndex)))); STATIC_ASSERT(OrderedHashMap::kLoadFactor == 2); Node* const capacity = WordShl(number_of_buckets.value(), 1); Node* const number_of_elements = SmiUntag( CAST(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset))); Node* const number_of_deleted = SmiUntag(CAST(LoadObjectField( table, OrderedHashMap::kNumberOfDeletedElementsOffset))); occupancy.Bind(IntPtrAdd(number_of_elements, number_of_deleted)); GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry); // We do not have enough space, grow the table and reload the relevant // fields. CallRuntime(Runtime::kMapGrow, context, receiver); table_var = CAST(LoadObjectField(receiver, JSMap::kTableOffset)); number_of_buckets.Bind(SmiUntag(CAST(LoadFixedArrayElement( table_var.value(), OrderedHashMap::kNumberOfBucketsIndex)))); Node* const new_number_of_elements = SmiUntag(CAST(LoadObjectField( table_var.value(), OrderedHashMap::kNumberOfElementsOffset))); Node* const new_number_of_deleted = SmiUntag(CAST(LoadObjectField( table_var.value(), OrderedHashMap::kNumberOfDeletedElementsOffset))); occupancy.Bind(IntPtrAdd(new_number_of_elements, new_number_of_deleted)); Goto(&store_new_entry); } BIND(&store_new_entry); // Store the key, value and connect the element to the bucket chain. StoreOrderedHashMapNewEntry(table_var.value(), key, value, entry_start_position_or_hash.value(), number_of_buckets.value(), occupancy.value()); Return(receiver); } void CollectionsBuiltinsAssembler::StoreOrderedHashMapNewEntry( TNode
const table, Node* const key, Node* const value, Node* const hash, Node* const number_of_buckets, Node* const occupancy) { Node* const bucket = WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1))); Node* const bucket_entry = LoadFixedArrayElement( table, bucket, OrderedHashMap::kHashTableStartIndex * kPointerSize); // Store the entry elements. Node* const entry_start = IntPtrAdd( IntPtrMul(occupancy, IntPtrConstant(OrderedHashMap::kEntrySize)), number_of_buckets); StoreFixedArrayElement(table, entry_start, key, UPDATE_WRITE_BARRIER, kPointerSize * OrderedHashMap::kHashTableStartIndex); StoreFixedArrayElement(table, entry_start, value, UPDATE_WRITE_BARRIER, kPointerSize * (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset)); StoreFixedArrayElement(table, entry_start, bucket_entry, SKIP_WRITE_BARRIER, kPointerSize * (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kChainOffset)); // Update the bucket head. StoreFixedArrayElement(table, bucket, SmiTag(occupancy), SKIP_WRITE_BARRIER, OrderedHashMap::kHashTableStartIndex * kPointerSize); // Bump the elements count. TNode
const number_of_elements = CAST(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset)); StoreObjectFieldNoWriteBarrier(table, OrderedHashMap::kNumberOfElementsOffset, SmiAdd(number_of_elements, SmiConstant(1))); } TF_BUILTIN(MapPrototypeDelete, CollectionsBuiltinsAssembler) { Node* const receiver = Parameter(Descriptor::kReceiver); Node* key = Parameter(Descriptor::kKey); Node* const context = Parameter(Descriptor::kContext); ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.delete"); TNode
const table = CAST(LoadObjectField(receiver, JSMap::kTableOffset)); VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(), IntPtrConstant(0)); Label entry_found(this), not_found(this); TryLookupOrderedHashTableIndex
(table, key, context, &entry_start_position_or_hash, &entry_found, ¬_found); BIND(¬_found); Return(FalseConstant()); BIND(&entry_found); // If we found the entry, mark the entry as deleted. StoreFixedArrayElement(table, entry_start_position_or_hash.value(), TheHoleConstant(), UPDATE_WRITE_BARRIER, kPointerSize * OrderedHashMap::kHashTableStartIndex); StoreFixedArrayElement(table, entry_start_position_or_hash.value(), TheHoleConstant(), UPDATE_WRITE_BARRIER, kPointerSize * (OrderedHashMap::kHashTableStartIndex + OrderedHashMap::kValueOffset)); // Decrement the number of elements, increment the number of deleted elements. TNode
const number_of_elements = SmiSub( CAST(LoadObjectField(table, OrderedHashMap::kNumberOfElementsOffset)), SmiConstant(1)); StoreObjectFieldNoWriteBarrier(table, OrderedHashMap::kNumberOfElementsOffset, number_of_elements); TNode
const number_of_deleted = SmiAdd(CAST(LoadObjectField( table, OrderedHashMap::kNumberOfDeletedElementsOffset)), SmiConstant(1)); StoreObjectFieldNoWriteBarrier( table, OrderedHashMap::kNumberOfDeletedElementsOffset, number_of_deleted); TNode
const number_of_buckets = CAST(LoadFixedArrayElement(table, OrderedHashMap::kNumberOfBucketsIndex)); // If there fewer elements than #buckets / 2, shrink the table. Label shrink(this); GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements), number_of_buckets), &shrink); Return(TrueConstant()); BIND(&shrink); CallRuntime(Runtime::kMapShrink, context, receiver); Return(TrueConstant()); } TF_BUILTIN(SetPrototypeAdd, CollectionsBuiltinsAssembler) { Node* const receiver = Parameter(Descriptor::kReceiver); Node* key = Parameter(Descriptor::kKey); Node* const context = Parameter(Descriptor::kContext); ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.add"); key = NormalizeNumberKey(key); TNode
const table = CAST(LoadObjectField(receiver, JSMap::kTableOffset)); VARIABLE(entry_start_position_or_hash, MachineType::PointerRepresentation(), IntPtrConstant(0)); Label entry_found(this), not_found(this); TryLookupOrderedHashTableIndex
(table, key, context, &entry_start_position_or_hash, &entry_found, ¬_found); BIND(&entry_found); // The entry was found, there is nothing to do. Return(receiver); Label no_hash(this), add_entry(this), store_new_entry(this); BIND(¬_found); { // If we have a hash code, we can start adding the new entry. GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(), IntPtrConstant(0)), &add_entry); // Otherwise, go to runtime to compute the hash code. entry_start_position_or_hash.Bind(SmiUntag(CallGetOrCreateHashRaw(key))); Goto(&add_entry); } BIND(&add_entry); VARIABLE(number_of_buckets, MachineType::PointerRepresentation()); VARIABLE(occupancy, MachineType::PointerRepresentation()); TVARIABLE(OrderedHashSet, table_var, table); { // Check we have enough space for the entry. number_of_buckets.Bind(SmiUntag(CAST( LoadFixedArrayElement(table, OrderedHashSet::kNumberOfBucketsIndex)))); STATIC_ASSERT(OrderedHashSet::kLoadFactor == 2); Node* const capacity = WordShl(number_of_buckets.value(), 1); Node* const number_of_elements = SmiUntag( CAST(LoadObjectField(table, OrderedHashSet::kNumberOfElementsOffset))); Node* const number_of_deleted = SmiUntag(CAST(LoadObjectField( table, OrderedHashSet::kNumberOfDeletedElementsOffset))); occupancy.Bind(IntPtrAdd(number_of_elements, number_of_deleted)); GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry); // We do not have enough space, grow the table and reload the relevant // fields. CallRuntime(Runtime::kSetGrow, context, receiver); table_var = CAST(LoadObjectField(receiver, JSMap::kTableOffset)); number_of_buckets.Bind(SmiUntag(CAST(LoadFixedArrayElement( table_var.value(), OrderedHashSet::kNumberOfBucketsIndex)))); Node* const new_number_of_elements = SmiUntag(CAST(LoadObjectField( table_var.value(), OrderedHashSet::kNumberOfElementsOffset))); Node* const new_number_of_deleted = SmiUntag(CAST(LoadObjectField( table_var.value(), OrderedHashSet::kNumberOfDeletedElementsOffset))); occupancy.Bind(IntPtrAdd(new_number_of_elements, new_number_of_deleted)); Goto(&store_new_entry); } BIND(&store_new_entry); // Store the key, value and connect the element to the bucket chain. StoreOrderedHashSetNewEntry(table_var.value(), key, entry_start_position_or_hash.value(), number_of_buckets.value(), occupancy.value()); Return(receiver); } void CollectionsBuiltinsAssembler::StoreOrderedHashSetNewEntry( TNode
const table, Node* const key, Node* const hash, Node* const number_of_buckets, Node* const occupancy) { Node* const bucket = WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1))); Node* const bucket_entry = LoadFixedArrayElement( table, bucket, OrderedHashSet::kHashTableStartIndex * kPointerSize); // Store the entry elements. Node* const entry_start = IntPtrAdd( IntPtrMul(occupancy, IntPtrConstant(OrderedHashSet::kEntrySize)), number_of_buckets); StoreFixedArrayElement(table, entry_start, key, UPDATE_WRITE_BARRIER, kPointerSize * OrderedHashSet::kHashTableStartIndex); StoreFixedArrayElement(table, entry_start, bucket_entry, SKIP_WRITE_BARRIER, kPointerSize * (OrderedHashSet::kHashTableStartIndex + OrderedHashSet::kChainOffset)); // Update the bucket head. StoreFixedArrayElement(table, bucket, SmiTag(occupancy), SKIP_WRITE_BARRIER, OrderedHashSet::kHashTableStartIndex * kPointerSize); // Bump the elements count. TNode