• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/builtins/builtins-collections-gen.h"
6 
7 #include "src/builtins/builtins-constructor-gen.h"
8 #include "src/builtins/builtins-iterator-gen.h"
9 #include "src/builtins/builtins-utils-gen.h"
10 #include "src/codegen/code-stub-assembler.h"
11 #include "src/execution/protectors.h"
12 #include "src/heap/factory-inl.h"
13 #include "src/heap/heap-inl.h"
14 #include "src/objects/hash-table-inl.h"
15 #include "src/objects/js-collection.h"
16 #include "src/objects/ordered-hash-table.h"
17 #include "src/roots/roots.h"
18 
19 namespace v8 {
20 namespace internal {
21 
22 template <class T>
23 using TVariable = compiler::TypedCodeAssemblerVariable<T>;
24 
25 class BaseCollectionsAssembler : public CodeStubAssembler {
26  public:
BaseCollectionsAssembler(compiler::CodeAssemblerState * state)27   explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state)
28       : CodeStubAssembler(state) {}
29 
30   virtual ~BaseCollectionsAssembler() = default;
31 
32  protected:
33   enum Variant { kMap, kSet, kWeakMap, kWeakSet };
34 
35   // Adds an entry to a collection.  For Maps, properly handles extracting the
36   // key and value from the entry (see LoadKeyValue()).
37   void AddConstructorEntry(Variant variant, TNode<Context> context,
38                            TNode<Object> collection, TNode<Object> add_function,
39                            TNode<Object> key_value,
40                            Label* if_may_have_side_effects = nullptr,
41                            Label* if_exception = nullptr,
42                            TVariable<Object>* var_exception = nullptr);
43 
44   // Adds constructor entries to a collection.  Choosing a fast path when
45   // possible.
46   void AddConstructorEntries(Variant variant, TNode<Context> context,
47                              TNode<Context> native_context,
48                              TNode<HeapObject> collection,
49                              TNode<Object> initial_entries);
50 
51   // Fast path for adding constructor entries.  Assumes the entries are a fast
52   // JS array (see CodeStubAssembler::BranchIfFastJSArray()).
53   void AddConstructorEntriesFromFastJSArray(Variant variant,
54                                             TNode<Context> context,
55                                             TNode<Context> native_context,
56                                             TNode<Object> collection,
57                                             TNode<JSArray> fast_jsarray,
58                                             Label* if_may_have_side_effects);
59 
60   // Adds constructor entries to a collection using the iterator protocol.
61   void AddConstructorEntriesFromIterable(Variant variant,
62                                          TNode<Context> context,
63                                          TNode<Context> native_context,
64                                          TNode<Object> collection,
65                                          TNode<Object> iterable);
66 
67   // Constructs a collection instance. Choosing a fast path when possible.
68   TNode<JSObject> AllocateJSCollection(TNode<Context> context,
69                                        TNode<JSFunction> constructor,
70                                        TNode<JSReceiver> new_target);
71 
72   // Fast path for constructing a collection instance if the constructor
73   // function has not been modified.
74   TNode<JSObject> AllocateJSCollectionFast(TNode<JSFunction> constructor);
75 
76   // Fallback for constructing a collection instance if the constructor function
77   // has been modified.
78   TNode<JSObject> AllocateJSCollectionSlow(TNode<Context> context,
79                                            TNode<JSFunction> constructor,
80                                            TNode<JSReceiver> new_target);
81 
82   // Allocates the backing store for a collection.
83   virtual TNode<HeapObject> AllocateTable(
84       Variant variant, TNode<IntPtrT> at_least_space_for) = 0;
85 
86   // Main entry point for a collection constructor builtin.
87   void GenerateConstructor(Variant variant,
88                            Handle<String> constructor_function_name,
89                            TNode<Object> new_target, TNode<IntPtrT> argc,
90                            TNode<Context> context);
91 
92   // Retrieves the collection function that adds an entry. `set` for Maps and
93   // `add` for Sets.
94   TNode<Object> GetAddFunction(Variant variant, TNode<Context> context,
95                                TNode<Object> collection);
96 
97   // Retrieves the collection constructor function.
98   TNode<JSFunction> GetConstructor(Variant variant,
99                                    TNode<Context> native_context);
100 
101   // Retrieves the initial collection function that adds an entry. Should only
102   // be called when it is certain that a collection prototype's map hasn't been
103   // changed.
104   TNode<JSFunction> GetInitialAddFunction(Variant variant,
105                                           TNode<Context> native_context);
106 
107   // Checks whether {collection}'s initial add/set function has been modified
108   // (depending on {variant}, loaded from {native_context}).
109   void GotoIfInitialAddFunctionModified(Variant variant,
110                                         TNode<NativeContext> native_context,
111                                         TNode<HeapObject> collection,
112                                         Label* if_modified);
113 
114   // Gets root index for the name of the add/set function.
115   RootIndex GetAddFunctionNameIndex(Variant variant);
116 
117   // Retrieves the offset to access the backing table from the collection.
118   int GetTableOffset(Variant variant);
119 
120   // Estimates the number of entries the collection will have after adding the
121   // entries passed in the constructor. AllocateTable() can use this to avoid
122   // the time of growing/rehashing when adding the constructor entries.
123   TNode<IntPtrT> EstimatedInitialSize(TNode<Object> initial_entries,
124                                       TNode<BoolT> is_fast_jsarray);
125 
126   void GotoIfCannotBeWeakKey(const TNode<Object> obj,
127                              Label* if_cannot_be_weak_key);
128 
129   // Determines whether the collection's prototype has been modified.
130   TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
131                                              TNode<Context> native_context,
132                                              TNode<Object> collection);
133 
134   // Gets the initial prototype map for given collection {variant}.
135   TNode<Map> GetInitialCollectionPrototype(Variant variant,
136                                            TNode<Context> native_context);
137 
138   // Loads an element from a fixed array.  If the element is the hole, returns
139   // `undefined`.
140   TNode<Object> LoadAndNormalizeFixedArrayElement(TNode<FixedArray> elements,
141                                                   TNode<IntPtrT> index);
142 
143   // Loads an element from a fixed double array.  If the element is the hole,
144   // returns `undefined`.
145   TNode<Object> LoadAndNormalizeFixedDoubleArrayElement(
146       TNode<HeapObject> elements, TNode<IntPtrT> index);
147 };
148 
AddConstructorEntry(Variant variant,TNode<Context> context,TNode<Object> collection,TNode<Object> add_function,TNode<Object> key_value,Label * if_may_have_side_effects,Label * if_exception,TVariable<Object> * var_exception)149 void BaseCollectionsAssembler::AddConstructorEntry(
150     Variant variant, TNode<Context> context, TNode<Object> collection,
151     TNode<Object> add_function, TNode<Object> key_value,
152     Label* if_may_have_side_effects, Label* if_exception,
153     TVariable<Object>* var_exception) {
154   compiler::ScopedExceptionHandler handler(this, if_exception, var_exception);
155   CSA_DCHECK(this, Word32BinaryNot(IsTheHole(key_value)));
156   if (variant == kMap || variant == kWeakMap) {
157     TorqueStructKeyValuePair pair =
158         if_may_have_side_effects != nullptr
159             ? LoadKeyValuePairNoSideEffects(context, key_value,
160                                             if_may_have_side_effects)
161             : LoadKeyValuePair(context, key_value);
162     TNode<Object> key_n = pair.key;
163     TNode<Object> value_n = pair.value;
164     Call(context, add_function, collection, key_n, value_n);
165   } else {
166     DCHECK(variant == kSet || variant == kWeakSet);
167     Call(context, add_function, collection, key_value);
168   }
169 }
170 
AddConstructorEntries(Variant variant,TNode<Context> context,TNode<Context> native_context,TNode<HeapObject> collection,TNode<Object> initial_entries)171 void BaseCollectionsAssembler::AddConstructorEntries(
172     Variant variant, TNode<Context> context, TNode<Context> native_context,
173     TNode<HeapObject> collection, TNode<Object> initial_entries) {
174   TVARIABLE(BoolT, use_fast_loop,
175             IsFastJSArrayWithNoCustomIteration(context, initial_entries));
176   TNode<IntPtrT> at_least_space_for =
177       EstimatedInitialSize(initial_entries, use_fast_loop.value());
178   Label allocate_table(this, &use_fast_loop), exit(this), fast_loop(this),
179       slow_loop(this, Label::kDeferred);
180   Goto(&allocate_table);
181   BIND(&allocate_table);
182   {
183     TNode<HeapObject> table = AllocateTable(variant, at_least_space_for);
184     StoreObjectField(collection, GetTableOffset(variant), table);
185     GotoIf(IsNullOrUndefined(initial_entries), &exit);
186     GotoIfInitialAddFunctionModified(variant, CAST(native_context), collection,
187                                      &slow_loop);
188     Branch(use_fast_loop.value(), &fast_loop, &slow_loop);
189   }
190   BIND(&fast_loop);
191   {
192     TNode<JSArray> initial_entries_jsarray =
193         UncheckedCast<JSArray>(initial_entries);
194 #if DEBUG
195     CSA_DCHECK(this, IsFastJSArrayWithNoCustomIteration(
196                          context, initial_entries_jsarray));
197     TNode<Map> original_initial_entries_map = LoadMap(initial_entries_jsarray);
198 #endif
199 
200     Label if_may_have_side_effects(this, Label::kDeferred);
201     AddConstructorEntriesFromFastJSArray(variant, context, native_context,
202                                          collection, initial_entries_jsarray,
203                                          &if_may_have_side_effects);
204     Goto(&exit);
205 
206     if (variant == kMap || variant == kWeakMap) {
207       BIND(&if_may_have_side_effects);
208 #if DEBUG
209       {
210         // Check that add/set function has not been modified.
211         Label if_not_modified(this), if_modified(this);
212         GotoIfInitialAddFunctionModified(variant, CAST(native_context),
213                                          collection, &if_modified);
214         Goto(&if_not_modified);
215         BIND(&if_modified);
216         Unreachable();
217         BIND(&if_not_modified);
218       }
219       CSA_DCHECK(this, TaggedEqual(original_initial_entries_map,
220                                    LoadMap(initial_entries_jsarray)));
221 #endif
222       use_fast_loop = Int32FalseConstant();
223       Goto(&allocate_table);
224     }
225   }
226   BIND(&slow_loop);
227   {
228     AddConstructorEntriesFromIterable(variant, context, native_context,
229                                       collection, initial_entries);
230     Goto(&exit);
231   }
232   BIND(&exit);
233 }
234 
AddConstructorEntriesFromFastJSArray(Variant variant,TNode<Context> context,TNode<Context> native_context,TNode<Object> collection,TNode<JSArray> fast_jsarray,Label * if_may_have_side_effects)235 void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray(
236     Variant variant, TNode<Context> context, TNode<Context> native_context,
237     TNode<Object> collection, TNode<JSArray> fast_jsarray,
238     Label* if_may_have_side_effects) {
239   TNode<FixedArrayBase> elements = LoadElements(fast_jsarray);
240   TNode<Int32T> elements_kind = LoadElementsKind(fast_jsarray);
241   TNode<JSFunction> add_func = GetInitialAddFunction(variant, native_context);
242   CSA_DCHECK(this,
243              TaggedEqual(GetAddFunction(variant, native_context, collection),
244                          add_func));
245   CSA_DCHECK(this, IsFastJSArrayWithNoCustomIteration(context, fast_jsarray));
246   TNode<IntPtrT> length = SmiUntag(LoadFastJSArrayLength(fast_jsarray));
247   CSA_DCHECK(this, IntPtrGreaterThanOrEqual(length, IntPtrConstant(0)));
248   CSA_DCHECK(
249       this, HasInitialCollectionPrototype(variant, native_context, collection));
250 
251 #if DEBUG
252   TNode<Map> original_collection_map = LoadMap(CAST(collection));
253   TNode<Map> original_fast_js_array_map = LoadMap(fast_jsarray);
254 #endif
255   Label exit(this), if_doubles(this), if_smiorobjects(this);
256   GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &exit);
257   Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects,
258          &if_doubles);
259   BIND(&if_smiorobjects);
260   {
261     auto set_entry = [&](TNode<IntPtrT> index) {
262       TNode<Object> element = LoadAndNormalizeFixedArrayElement(
263           CAST(elements), UncheckedCast<IntPtrT>(index));
264       AddConstructorEntry(variant, context, collection, add_func, element,
265                           if_may_have_side_effects);
266     };
267 
268     // Instead of using the slower iteration protocol to iterate over the
269     // elements, a fast loop is used.  This assumes that adding an element
270     // to the collection does not call user code that could mutate the elements
271     // or collection.
272     BuildFastLoop<IntPtrT>(IntPtrConstant(0), length, set_entry, 1,
273                            IndexAdvanceMode::kPost);
274     Goto(&exit);
275   }
276   BIND(&if_doubles);
277   {
278     // A Map constructor requires entries to be arrays (ex. [key, value]),
279     // so a FixedDoubleArray can never succeed.
280     if (variant == kMap || variant == kWeakMap) {
281       CSA_DCHECK(this, IntPtrGreaterThan(length, IntPtrConstant(0)));
282       TNode<Object> element =
283           LoadAndNormalizeFixedDoubleArrayElement(elements, IntPtrConstant(0));
284       ThrowTypeError(context, MessageTemplate::kIteratorValueNotAnObject,
285                      element);
286     } else {
287       DCHECK(variant == kSet || variant == kWeakSet);
288       auto set_entry = [&](TNode<IntPtrT> index) {
289         TNode<Object> entry = LoadAndNormalizeFixedDoubleArrayElement(
290             elements, UncheckedCast<IntPtrT>(index));
291         AddConstructorEntry(variant, context, collection, add_func, entry);
292       };
293       BuildFastLoop<IntPtrT>(IntPtrConstant(0), length, set_entry, 1,
294                              IndexAdvanceMode::kPost);
295       Goto(&exit);
296     }
297   }
298   BIND(&exit);
299 #if DEBUG
300   CSA_DCHECK(this,
301              TaggedEqual(original_collection_map, LoadMap(CAST(collection))));
302   CSA_DCHECK(this,
303              TaggedEqual(original_fast_js_array_map, LoadMap(fast_jsarray)));
304 #endif
305 }
306 
AddConstructorEntriesFromIterable(Variant variant,TNode<Context> context,TNode<Context> native_context,TNode<Object> collection,TNode<Object> iterable)307 void BaseCollectionsAssembler::AddConstructorEntriesFromIterable(
308     Variant variant, TNode<Context> context, TNode<Context> native_context,
309     TNode<Object> collection, TNode<Object> iterable) {
310   Label exit(this), loop(this), if_exception(this, Label::kDeferred);
311   CSA_DCHECK(this, Word32BinaryNot(IsNullOrUndefined(iterable)));
312 
313   TNode<Object> add_func = GetAddFunction(variant, context, collection);
314   IteratorBuiltinsAssembler iterator_assembler(this->state());
315   TorqueStructIteratorRecord iterator =
316       iterator_assembler.GetIterator(context, iterable);
317 
318   CSA_DCHECK(this, Word32BinaryNot(IsUndefined(iterator.object)));
319 
320   TNode<Map> fast_iterator_result_map = CAST(
321       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX));
322   TVARIABLE(Object, var_exception);
323 
324   Goto(&loop);
325   BIND(&loop);
326   {
327     TNode<JSReceiver> next = iterator_assembler.IteratorStep(
328         context, iterator, &exit, fast_iterator_result_map);
329     TNode<Object> next_value = iterator_assembler.IteratorValue(
330         context, next, fast_iterator_result_map);
331     AddConstructorEntry(variant, context, collection, add_func, next_value,
332                         nullptr, &if_exception, &var_exception);
333     Goto(&loop);
334   }
335   BIND(&if_exception);
336   {
337     TNode<HeapObject> message = GetPendingMessage();
338     SetPendingMessage(TheHoleConstant());
339     IteratorCloseOnException(context, iterator);
340     CallRuntime(Runtime::kReThrowWithMessage, context, var_exception.value(),
341                 message);
342     Unreachable();
343   }
344   BIND(&exit);
345 }
346 
GetAddFunctionNameIndex(Variant variant)347 RootIndex BaseCollectionsAssembler::GetAddFunctionNameIndex(Variant variant) {
348   switch (variant) {
349     case kMap:
350     case kWeakMap:
351       return RootIndex::kset_string;
352     case kSet:
353     case kWeakSet:
354       return RootIndex::kadd_string;
355   }
356   UNREACHABLE();
357 }
358 
GotoIfInitialAddFunctionModified(Variant variant,TNode<NativeContext> native_context,TNode<HeapObject> collection,Label * if_modified)359 void BaseCollectionsAssembler::GotoIfInitialAddFunctionModified(
360     Variant variant, TNode<NativeContext> native_context,
361     TNode<HeapObject> collection, Label* if_modified) {
362   STATIC_ASSERT(JSCollection::kAddFunctionDescriptorIndex ==
363                 JSWeakCollection::kAddFunctionDescriptorIndex);
364 
365   // TODO(jgruber): Investigate if this should also fall back to full prototype
366   // verification.
367   static constexpr PrototypeCheckAssembler::Flags flags{
368       PrototypeCheckAssembler::kCheckPrototypePropertyConstness};
369 
370   static constexpr int kNoContextIndex = -1;
371   STATIC_ASSERT(
372       (flags & PrototypeCheckAssembler::kCheckPrototypePropertyIdentity) == 0);
373 
374   using DescriptorIndexNameValue =
375       PrototypeCheckAssembler::DescriptorIndexNameValue;
376 
377   DescriptorIndexNameValue property_to_check{
378       JSCollection::kAddFunctionDescriptorIndex,
379       GetAddFunctionNameIndex(variant), kNoContextIndex};
380 
381   PrototypeCheckAssembler prototype_check_assembler(
382       state(), flags, native_context,
383       GetInitialCollectionPrototype(variant, native_context),
384       base::Vector<DescriptorIndexNameValue>(&property_to_check, 1));
385 
386   TNode<HeapObject> prototype = LoadMapPrototype(LoadMap(collection));
387   Label if_unmodified(this);
388   prototype_check_assembler.CheckAndBranch(prototype, &if_unmodified,
389                                            if_modified);
390 
391   BIND(&if_unmodified);
392 }
393 
AllocateJSCollection(TNode<Context> context,TNode<JSFunction> constructor,TNode<JSReceiver> new_target)394 TNode<JSObject> BaseCollectionsAssembler::AllocateJSCollection(
395     TNode<Context> context, TNode<JSFunction> constructor,
396     TNode<JSReceiver> new_target) {
397   TNode<BoolT> is_target_unmodified = TaggedEqual(constructor, new_target);
398 
399   return Select<JSObject>(
400       is_target_unmodified,
401       [=] { return AllocateJSCollectionFast(constructor); },
402       [=] {
403         return AllocateJSCollectionSlow(context, constructor, new_target);
404       });
405 }
406 
AllocateJSCollectionFast(TNode<JSFunction> constructor)407 TNode<JSObject> BaseCollectionsAssembler::AllocateJSCollectionFast(
408     TNode<JSFunction> constructor) {
409   CSA_DCHECK(this, IsConstructorMap(LoadMap(constructor)));
410   TNode<Map> initial_map =
411       CAST(LoadJSFunctionPrototypeOrInitialMap(constructor));
412   return AllocateJSObjectFromMap(initial_map);
413 }
414 
AllocateJSCollectionSlow(TNode<Context> context,TNode<JSFunction> constructor,TNode<JSReceiver> new_target)415 TNode<JSObject> BaseCollectionsAssembler::AllocateJSCollectionSlow(
416     TNode<Context> context, TNode<JSFunction> constructor,
417     TNode<JSReceiver> new_target) {
418   ConstructorBuiltinsAssembler constructor_assembler(this->state());
419   return constructor_assembler.FastNewObject(context, constructor, new_target);
420 }
421 
GenerateConstructor(Variant variant,Handle<String> constructor_function_name,TNode<Object> new_target,TNode<IntPtrT> argc,TNode<Context> context)422 void BaseCollectionsAssembler::GenerateConstructor(
423     Variant variant, Handle<String> constructor_function_name,
424     TNode<Object> new_target, TNode<IntPtrT> argc, TNode<Context> context) {
425   const int kIterableArg = 0;
426   CodeStubArguments args(this, argc);
427   TNode<Object> iterable = args.GetOptionalArgumentValue(kIterableArg);
428 
429   Label if_undefined(this, Label::kDeferred);
430   GotoIf(IsUndefined(new_target), &if_undefined);
431 
432   TNode<NativeContext> native_context = LoadNativeContext(context);
433   TNode<JSObject> collection = AllocateJSCollection(
434       context, GetConstructor(variant, native_context), CAST(new_target));
435 
436   AddConstructorEntries(variant, context, native_context, collection, iterable);
437   Return(collection);
438 
439   BIND(&if_undefined);
440   ThrowTypeError(context, MessageTemplate::kConstructorNotFunction,
441                  HeapConstant(constructor_function_name));
442 }
443 
GetAddFunction(Variant variant,TNode<Context> context,TNode<Object> collection)444 TNode<Object> BaseCollectionsAssembler::GetAddFunction(
445     Variant variant, TNode<Context> context, TNode<Object> collection) {
446   Handle<String> add_func_name = (variant == kMap || variant == kWeakMap)
447                                      ? isolate()->factory()->set_string()
448                                      : isolate()->factory()->add_string();
449   TNode<Object> add_func = GetProperty(context, collection, add_func_name);
450 
451   Label exit(this), if_notcallable(this, Label::kDeferred);
452   GotoIf(TaggedIsSmi(add_func), &if_notcallable);
453   GotoIfNot(IsCallable(CAST(add_func)), &if_notcallable);
454   Goto(&exit);
455 
456   BIND(&if_notcallable);
457   ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, add_func,
458                  HeapConstant(add_func_name), collection);
459 
460   BIND(&exit);
461   return add_func;
462 }
463 
GetConstructor(Variant variant,TNode<Context> native_context)464 TNode<JSFunction> BaseCollectionsAssembler::GetConstructor(
465     Variant variant, TNode<Context> native_context) {
466   int index;
467   switch (variant) {
468     case kMap:
469       index = Context::JS_MAP_FUN_INDEX;
470       break;
471     case kSet:
472       index = Context::JS_SET_FUN_INDEX;
473       break;
474     case kWeakMap:
475       index = Context::JS_WEAK_MAP_FUN_INDEX;
476       break;
477     case kWeakSet:
478       index = Context::JS_WEAK_SET_FUN_INDEX;
479       break;
480   }
481   return CAST(LoadContextElement(native_context, index));
482 }
483 
GetInitialAddFunction(Variant variant,TNode<Context> native_context)484 TNode<JSFunction> BaseCollectionsAssembler::GetInitialAddFunction(
485     Variant variant, TNode<Context> native_context) {
486   int index;
487   switch (variant) {
488     case kMap:
489       index = Context::MAP_SET_INDEX;
490       break;
491     case kSet:
492       index = Context::SET_ADD_INDEX;
493       break;
494     case kWeakMap:
495       index = Context::WEAKMAP_SET_INDEX;
496       break;
497     case kWeakSet:
498       index = Context::WEAKSET_ADD_INDEX;
499       break;
500   }
501   return CAST(LoadContextElement(native_context, index));
502 }
503 
GetTableOffset(Variant variant)504 int BaseCollectionsAssembler::GetTableOffset(Variant variant) {
505   switch (variant) {
506     case kMap:
507       return JSMap::kTableOffset;
508     case kSet:
509       return JSSet::kTableOffset;
510     case kWeakMap:
511       return JSWeakMap::kTableOffset;
512     case kWeakSet:
513       return JSWeakSet::kTableOffset;
514   }
515   UNREACHABLE();
516 }
517 
EstimatedInitialSize(TNode<Object> initial_entries,TNode<BoolT> is_fast_jsarray)518 TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(
519     TNode<Object> initial_entries, TNode<BoolT> is_fast_jsarray) {
520   return Select<IntPtrT>(
521       is_fast_jsarray,
522       [=] { return SmiUntag(LoadFastJSArrayLength(CAST(initial_entries))); },
523       [=] { return IntPtrConstant(0); });
524 }
525 
GotoIfCannotBeWeakKey(const TNode<Object> obj,Label * if_cannot_be_weak_key)526 void BaseCollectionsAssembler::GotoIfCannotBeWeakKey(
527     const TNode<Object> obj, Label* if_cannot_be_weak_key) {
528   GotoIf(TaggedIsSmi(obj), if_cannot_be_weak_key);
529   TNode<Uint16T> instance_type = LoadMapInstanceType(LoadMap(CAST(obj)));
530   GotoIfNot(IsJSReceiverInstanceType(instance_type), if_cannot_be_weak_key);
531   // TODO(v8:12547) Shared structs should only be able to point to shared values
532   // in weak collections. For now, disallow them as weak collection keys.
533   GotoIf(IsJSSharedStructInstanceType(instance_type), if_cannot_be_weak_key);
534 }
535 
GetInitialCollectionPrototype(Variant variant,TNode<Context> native_context)536 TNode<Map> BaseCollectionsAssembler::GetInitialCollectionPrototype(
537     Variant variant, TNode<Context> native_context) {
538   int initial_prototype_index;
539   switch (variant) {
540     case kMap:
541       initial_prototype_index = Context::INITIAL_MAP_PROTOTYPE_MAP_INDEX;
542       break;
543     case kSet:
544       initial_prototype_index = Context::INITIAL_SET_PROTOTYPE_MAP_INDEX;
545       break;
546     case kWeakMap:
547       initial_prototype_index = Context::INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX;
548       break;
549     case kWeakSet:
550       initial_prototype_index = Context::INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX;
551       break;
552   }
553   return CAST(LoadContextElement(native_context, initial_prototype_index));
554 }
555 
HasInitialCollectionPrototype(Variant variant,TNode<Context> native_context,TNode<Object> collection)556 TNode<BoolT> BaseCollectionsAssembler::HasInitialCollectionPrototype(
557     Variant variant, TNode<Context> native_context, TNode<Object> collection) {
558   TNode<Map> collection_proto_map =
559       LoadMap(LoadMapPrototype(LoadMap(CAST(collection))));
560 
561   return TaggedEqual(collection_proto_map,
562                      GetInitialCollectionPrototype(variant, native_context));
563 }
564 
LoadAndNormalizeFixedArrayElement(TNode<FixedArray> elements,TNode<IntPtrT> index)565 TNode<Object> BaseCollectionsAssembler::LoadAndNormalizeFixedArrayElement(
566     TNode<FixedArray> elements, TNode<IntPtrT> index) {
567   TNode<Object> element = UnsafeLoadFixedArrayElement(elements, index);
568   return Select<Object>(IsTheHole(element), [=] { return UndefinedConstant(); },
569                         [=] { return element; });
570 }
571 
LoadAndNormalizeFixedDoubleArrayElement(TNode<HeapObject> elements,TNode<IntPtrT> index)572 TNode<Object> BaseCollectionsAssembler::LoadAndNormalizeFixedDoubleArrayElement(
573     TNode<HeapObject> elements, TNode<IntPtrT> index) {
574   TVARIABLE(Object, entry);
575   Label if_hole(this, Label::kDeferred), next(this);
576   TNode<Float64T> element =
577       LoadFixedDoubleArrayElement(CAST(elements), index, &if_hole);
578   {  // not hole
579     entry = AllocateHeapNumberWithValue(element);
580     Goto(&next);
581   }
582   BIND(&if_hole);
583   {
584     entry = UndefinedConstant();
585     Goto(&next);
586   }
587   BIND(&next);
588   return entry.value();
589 }
590 
591 class CollectionsBuiltinsAssembler : public BaseCollectionsAssembler {
592  public:
CollectionsBuiltinsAssembler(compiler::CodeAssemblerState * state)593   explicit CollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state)
594       : BaseCollectionsAssembler(state) {}
595 
596   // Check whether |iterable| is a JS_MAP_KEY_ITERATOR_TYPE or
597   // JS_MAP_VALUE_ITERATOR_TYPE object that is not partially consumed and still
598   // has original iteration behavior.
599   void BranchIfIterableWithOriginalKeyOrValueMapIterator(TNode<Object> iterable,
600                                                          TNode<Context> context,
601                                                          Label* if_true,
602                                                          Label* if_false);
603 
604   // Check whether |iterable| is a JS_SET_TYPE or JS_SET_VALUE_ITERATOR_TYPE
605   // object that still has original iteration behavior. In case of the iterator,
606   // the iterator also must not have been partially consumed.
607   void BranchIfIterableWithOriginalValueSetIterator(TNode<Object> iterable,
608                                                     TNode<Context> context,
609                                                     Label* if_true,
610                                                     Label* if_false);
611 
612  protected:
613   template <typename IteratorType>
614   TNode<HeapObject> AllocateJSCollectionIterator(
615       const TNode<Context> context, int map_index,
616       const TNode<HeapObject> collection);
617   TNode<HeapObject> AllocateTable(Variant variant,
618                                   TNode<IntPtrT> at_least_space_for) override;
619   TNode<IntPtrT> GetHash(const TNode<HeapObject> key);
620   TNode<IntPtrT> CallGetHashRaw(const TNode<HeapObject> key);
621   TNode<Smi> CallGetOrCreateHashRaw(const TNode<HeapObject> key);
622 
623   // Transitions the iterator to the non obsolete backing store.
624   // This is a NOP if the [table] is not obsolete.
625   template <typename TableType>
626   using UpdateInTransition = std::function<void(const TNode<TableType> table,
627                                                 const TNode<IntPtrT> index)>;
628   template <typename TableType>
629   std::pair<TNode<TableType>, TNode<IntPtrT>> Transition(
630       const TNode<TableType> table, const TNode<IntPtrT> index,
631       UpdateInTransition<TableType> const& update_in_transition);
632   template <typename IteratorType, typename TableType>
633   std::pair<TNode<TableType>, TNode<IntPtrT>> TransitionAndUpdate(
634       const TNode<IteratorType> iterator);
635   template <typename TableType>
636   std::tuple<TNode<Object>, TNode<IntPtrT>, TNode<IntPtrT>> NextSkipHoles(
637       TNode<TableType> table, TNode<IntPtrT> index, Label* if_end);
638 
639   // Specialization for Smi.
640   // The {result} variable will contain the entry index if the key was found,
641   // or the hash code otherwise.
642   template <typename CollectionType>
643   void FindOrderedHashTableEntryForSmiKey(TNode<CollectionType> table,
644                                           TNode<Smi> key_tagged,
645                                           TVariable<IntPtrT>* result,
646                                           Label* entry_found, Label* not_found);
647   void SameValueZeroSmi(TNode<Smi> key_smi, TNode<Object> candidate_key,
648                         Label* if_same, Label* if_not_same);
649 
650   // Specialization for heap numbers.
651   // The {result} variable will contain the entry index if the key was found,
652   // or the hash code otherwise.
653   void SameValueZeroHeapNumber(TNode<Float64T> key_float,
654                                TNode<Object> candidate_key, Label* if_same,
655                                Label* if_not_same);
656   template <typename CollectionType>
657   void FindOrderedHashTableEntryForHeapNumberKey(
658       TNode<CollectionType> table, TNode<HeapNumber> key_heap_number,
659       TVariable<IntPtrT>* result, Label* entry_found, Label* not_found);
660 
661   // Specialization for bigints.
662   // The {result} variable will contain the entry index if the key was found,
663   // or the hash code otherwise.
664   void SameValueZeroBigInt(TNode<BigInt> key, TNode<Object> candidate_key,
665                            Label* if_same, Label* if_not_same);
666   template <typename CollectionType>
667   void FindOrderedHashTableEntryForBigIntKey(TNode<CollectionType> table,
668                                              TNode<BigInt> key_big_int,
669                                              TVariable<IntPtrT>* result,
670                                              Label* entry_found,
671                                              Label* not_found);
672 
673   // Specialization for string.
674   // The {result} variable will contain the entry index if the key was found,
675   // or the hash code otherwise.
676   template <typename CollectionType>
677   void FindOrderedHashTableEntryForStringKey(TNode<CollectionType> table,
678                                              TNode<String> key_tagged,
679                                              TVariable<IntPtrT>* result,
680                                              Label* entry_found,
681                                              Label* not_found);
682   TNode<IntPtrT> ComputeStringHash(TNode<String> string_key);
683   void SameValueZeroString(TNode<String> key_string,
684                            TNode<Object> candidate_key, Label* if_same,
685                            Label* if_not_same);
686 
687   // Specialization for non-strings, non-numbers. For those we only need
688   // reference equality to compare the keys.
689   // The {result} variable will contain the entry index if the key was found,
690   // or the hash code otherwise. If the hash-code has not been computed, it
691   // should be Smi -1.
692   template <typename CollectionType>
693   void FindOrderedHashTableEntryForOtherKey(TNode<CollectionType> table,
694                                             TNode<HeapObject> key_heap_object,
695                                             TVariable<IntPtrT>* result,
696                                             Label* entry_found,
697                                             Label* not_found);
698 
699   template <typename CollectionType>
700   void TryLookupOrderedHashTableIndex(const TNode<CollectionType> table,
701                                       const TNode<Object> key,
702                                       TVariable<IntPtrT>* result,
703                                       Label* if_entry_found,
704                                       Label* if_not_found);
705 
706   const TNode<Object> NormalizeNumberKey(const TNode<Object> key);
707   void StoreOrderedHashMapNewEntry(const TNode<OrderedHashMap> table,
708                                    const TNode<Object> key,
709                                    const TNode<Object> value,
710                                    const TNode<IntPtrT> hash,
711                                    const TNode<IntPtrT> number_of_buckets,
712                                    const TNode<IntPtrT> occupancy);
713 
714   void StoreOrderedHashSetNewEntry(const TNode<OrderedHashSet> table,
715                                    const TNode<Object> key,
716                                    const TNode<IntPtrT> hash,
717                                    const TNode<IntPtrT> number_of_buckets,
718                                    const TNode<IntPtrT> occupancy);
719 
720   // Create a JSArray with PACKED_ELEMENTS kind from a Map.prototype.keys() or
721   // Map.prototype.values() iterator. The iterator is assumed to satisfy
722   // IterableWithOriginalKeyOrValueMapIterator. This function will skip the
723   // iterator and iterate directly on the underlying hash table. In the end it
724   // will update the state of the iterator to 'exhausted'.
725   TNode<JSArray> MapIteratorToList(TNode<Context> context,
726                                    TNode<JSMapIterator> iterator);
727 
728   // Create a JSArray with PACKED_ELEMENTS kind from a Set.prototype.keys() or
729   // Set.prototype.values() iterator, or a Set. The |iterable| is assumed to
730   // satisfy IterableWithOriginalValueSetIterator. This function will skip the
731   // iterator and iterate directly on the underlying hash table. In the end, if
732   // |iterable| is an iterator, it will update the state of the iterator to
733   // 'exhausted'.
734   TNode<JSArray> SetOrSetIteratorToList(TNode<Context> context,
735                                         TNode<HeapObject> iterable);
736 
737   void BranchIfMapIteratorProtectorValid(Label* if_true, Label* if_false);
738   void BranchIfSetIteratorProtectorValid(Label* if_true, Label* if_false);
739 
740   // Builds code that finds OrderedHashTable entry for a key with hash code
741   // {hash} with using the comparison code generated by {key_compare}. The code
742   // jumps to {entry_found} if the key is found, or to {not_found} if the key
743   // was not found. In the {entry_found} branch, the variable
744   // entry_start_position will be bound to the index of the entry (relative to
745   // OrderedHashTable::kHashTableStartIndex).
746   //
747   // The {CollectionType} template parameter stands for the particular instance
748   // of OrderedHashTable, it should be OrderedHashMap or OrderedHashSet.
749   template <typename CollectionType>
750   void FindOrderedHashTableEntry(
751       const TNode<CollectionType> table, const TNode<IntPtrT> hash,
752       const std::function<void(TNode<Object>, Label*, Label*)>& key_compare,
753       TVariable<IntPtrT>* entry_start_position, Label* entry_found,
754       Label* not_found);
755 
756   TNode<Word32T> ComputeUnseededHash(TNode<IntPtrT> key);
757 };
758 
759 template <typename CollectionType>
FindOrderedHashTableEntry(const TNode<CollectionType> table,const TNode<IntPtrT> hash,const std::function<void (TNode<Object>,Label *,Label *)> & key_compare,TVariable<IntPtrT> * entry_start_position,Label * entry_found,Label * not_found)760 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntry(
761     const TNode<CollectionType> table, const TNode<IntPtrT> hash,
762     const std::function<void(TNode<Object>, Label*, Label*)>& key_compare,
763     TVariable<IntPtrT>* entry_start_position, Label* entry_found,
764     Label* not_found) {
765   // Get the index of the bucket.
766   const TNode<IntPtrT> number_of_buckets =
767       SmiUntag(CAST(UnsafeLoadFixedArrayElement(
768           table, CollectionType::NumberOfBucketsIndex())));
769   const TNode<IntPtrT> bucket =
770       WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1)));
771   const TNode<IntPtrT> first_entry = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
772       table, bucket, CollectionType::HashTableStartIndex() * kTaggedSize)));
773 
774   // Walk the bucket chain.
775   TNode<IntPtrT> entry_start;
776   Label if_key_found(this);
777   {
778     TVARIABLE(IntPtrT, var_entry, first_entry);
779     Label loop(this, {&var_entry, entry_start_position}),
780         continue_next_entry(this);
781     Goto(&loop);
782     BIND(&loop);
783 
784     // If the entry index is the not-found sentinel, we are done.
785     GotoIf(IntPtrEqual(var_entry.value(),
786                        IntPtrConstant(CollectionType::kNotFound)),
787            not_found);
788 
789     // Make sure the entry index is within range.
790     CSA_DCHECK(
791         this,
792         UintPtrLessThan(
793             var_entry.value(),
794             SmiUntag(SmiAdd(
795                 CAST(UnsafeLoadFixedArrayElement(
796                     table, CollectionType::NumberOfElementsIndex())),
797                 CAST(UnsafeLoadFixedArrayElement(
798                     table, CollectionType::NumberOfDeletedElementsIndex()))))));
799 
800     // Compute the index of the entry relative to kHashTableStartIndex.
801     entry_start =
802         IntPtrAdd(IntPtrMul(var_entry.value(),
803                             IntPtrConstant(CollectionType::kEntrySize)),
804                   number_of_buckets);
805 
806     // Load the key from the entry.
807     const TNode<Object> candidate_key = UnsafeLoadFixedArrayElement(
808         table, entry_start,
809         CollectionType::HashTableStartIndex() * kTaggedSize);
810 
811     key_compare(candidate_key, &if_key_found, &continue_next_entry);
812 
813     BIND(&continue_next_entry);
814     // Load the index of the next entry in the bucket chain.
815     var_entry = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
816         table, entry_start,
817         (CollectionType::HashTableStartIndex() + CollectionType::kChainOffset) *
818             kTaggedSize)));
819 
820     Goto(&loop);
821   }
822 
823   BIND(&if_key_found);
824   *entry_start_position = entry_start;
825   Goto(entry_found);
826 }
827 
828 template <typename IteratorType>
AllocateJSCollectionIterator(const TNode<Context> context,int map_index,const TNode<HeapObject> collection)829 TNode<HeapObject> CollectionsBuiltinsAssembler::AllocateJSCollectionIterator(
830     const TNode<Context> context, int map_index,
831     const TNode<HeapObject> collection) {
832   const TNode<Object> table =
833       LoadObjectField(collection, JSCollection::kTableOffset);
834   const TNode<NativeContext> native_context = LoadNativeContext(context);
835   const TNode<Map> iterator_map =
836       CAST(LoadContextElement(native_context, map_index));
837   const TNode<HeapObject> iterator =
838       AllocateInNewSpace(IteratorType::kHeaderSize);
839   StoreMapNoWriteBarrier(iterator, iterator_map);
840   StoreObjectFieldRoot(iterator, IteratorType::kPropertiesOrHashOffset,
841                        RootIndex::kEmptyFixedArray);
842   StoreObjectFieldRoot(iterator, IteratorType::kElementsOffset,
843                        RootIndex::kEmptyFixedArray);
844   StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kTableOffset, table);
845   StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset,
846                                  SmiConstant(0));
847   return iterator;
848 }
849 
AllocateTable(Variant variant,TNode<IntPtrT> at_least_space_for)850 TNode<HeapObject> CollectionsBuiltinsAssembler::AllocateTable(
851     Variant variant, TNode<IntPtrT> at_least_space_for) {
852   if (variant == kMap || variant == kWeakMap) {
853     return AllocateOrderedHashMap();
854   } else {
855     return AllocateOrderedHashSet();
856   }
857 }
858 
TF_BUILTIN(MapConstructor,CollectionsBuiltinsAssembler)859 TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
860   auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
861   TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
862       UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
863   auto context = Parameter<Context>(Descriptor::kContext);
864 
865   GenerateConstructor(kMap, isolate()->factory()->Map_string(), new_target,
866                       argc, context);
867 }
868 
TF_BUILTIN(SetConstructor,CollectionsBuiltinsAssembler)869 TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
870   auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
871   TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
872       UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
873   auto context = Parameter<Context>(Descriptor::kContext);
874 
875   GenerateConstructor(kSet, isolate()->factory()->Set_string(), new_target,
876                       argc, context);
877 }
878 
CallGetOrCreateHashRaw(const TNode<HeapObject> key)879 TNode<Smi> CollectionsBuiltinsAssembler::CallGetOrCreateHashRaw(
880     const TNode<HeapObject> key) {
881   const TNode<ExternalReference> function_addr =
882       ExternalConstant(ExternalReference::get_or_create_hash_raw());
883   const TNode<ExternalReference> isolate_ptr =
884       ExternalConstant(ExternalReference::isolate_address(isolate()));
885 
886   MachineType type_ptr = MachineType::Pointer();
887   MachineType type_tagged = MachineType::AnyTagged();
888 
889   TNode<Smi> result = CAST(CallCFunction(function_addr, type_tagged,
890                                          std::make_pair(type_ptr, isolate_ptr),
891                                          std::make_pair(type_tagged, key)));
892 
893   return result;
894 }
895 
CallGetHashRaw(const TNode<HeapObject> key)896 TNode<IntPtrT> CollectionsBuiltinsAssembler::CallGetHashRaw(
897     const TNode<HeapObject> key) {
898   const TNode<ExternalReference> function_addr =
899       ExternalConstant(ExternalReference::orderedhashmap_gethash_raw());
900   const TNode<ExternalReference> isolate_ptr =
901       ExternalConstant(ExternalReference::isolate_address(isolate()));
902 
903   MachineType type_ptr = MachineType::Pointer();
904   MachineType type_tagged = MachineType::AnyTagged();
905 
906   TNode<Smi> result = CAST(CallCFunction(function_addr, type_tagged,
907                                          std::make_pair(type_ptr, isolate_ptr),
908                                          std::make_pair(type_tagged, key)));
909 
910   return SmiUntag(result);
911 }
912 
GetHash(const TNode<HeapObject> key)913 TNode<IntPtrT> CollectionsBuiltinsAssembler::GetHash(
914     const TNode<HeapObject> key) {
915   TVARIABLE(IntPtrT, var_hash);
916   Label if_receiver(this), if_other(this), done(this);
917   Branch(IsJSReceiver(key), &if_receiver, &if_other);
918 
919   BIND(&if_receiver);
920   {
921     var_hash = LoadJSReceiverIdentityHash(CAST(key));
922     Goto(&done);
923   }
924 
925   BIND(&if_other);
926   {
927     var_hash = CallGetHashRaw(key);
928     Goto(&done);
929   }
930 
931   BIND(&done);
932   return var_hash.value();
933 }
934 
SameValueZeroSmi(TNode<Smi> key_smi,TNode<Object> candidate_key,Label * if_same,Label * if_not_same)935 void CollectionsBuiltinsAssembler::SameValueZeroSmi(TNode<Smi> key_smi,
936                                                     TNode<Object> candidate_key,
937                                                     Label* if_same,
938                                                     Label* if_not_same) {
939   // If the key is the same, we are done.
940   GotoIf(TaggedEqual(candidate_key, key_smi), if_same);
941 
942   // If the candidate key is smi, then it must be different (because
943   // we already checked for equality above).
944   GotoIf(TaggedIsSmi(candidate_key), if_not_same);
945 
946   // If the candidate key is not smi, we still have to check if it is a
947   // heap number with the same value.
948   GotoIfNot(IsHeapNumber(CAST(candidate_key)), if_not_same);
949 
950   const TNode<Float64T> candidate_key_number =
951       LoadHeapNumberValue(CAST(candidate_key));
952   const TNode<Float64T> key_number = SmiToFloat64(key_smi);
953 
954   GotoIf(Float64Equal(candidate_key_number, key_number), if_same);
955 
956   Goto(if_not_same);
957 }
958 
BranchIfMapIteratorProtectorValid(Label * if_true,Label * if_false)959 void CollectionsBuiltinsAssembler::BranchIfMapIteratorProtectorValid(
960     Label* if_true, Label* if_false) {
961   TNode<PropertyCell> protector_cell = MapIteratorProtectorConstant();
962   DCHECK(isolate()->heap()->map_iterator_protector().IsPropertyCell());
963   Branch(
964       TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
965                   SmiConstant(Protectors::kProtectorValid)),
966       if_true, if_false);
967 }
968 
969 void CollectionsBuiltinsAssembler::
BranchIfIterableWithOriginalKeyOrValueMapIterator(TNode<Object> iterator,TNode<Context> context,Label * if_true,Label * if_false)970     BranchIfIterableWithOriginalKeyOrValueMapIterator(TNode<Object> iterator,
971                                                       TNode<Context> context,
972                                                       Label* if_true,
973                                                       Label* if_false) {
974   Label if_key_or_value_iterator(this), extra_checks(this);
975 
976   // Check if iterator is a keys or values JSMapIterator.
977   GotoIf(TaggedIsSmi(iterator), if_false);
978   TNode<Map> iter_map = LoadMap(CAST(iterator));
979   const TNode<Uint16T> instance_type = LoadMapInstanceType(iter_map);
980   GotoIf(InstanceTypeEqual(instance_type, JS_MAP_KEY_ITERATOR_TYPE),
981          &if_key_or_value_iterator);
982   Branch(InstanceTypeEqual(instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
983          &if_key_or_value_iterator, if_false);
984 
985   BIND(&if_key_or_value_iterator);
986   // Check that the iterator is not partially consumed.
987   const TNode<Object> index =
988       LoadObjectField(CAST(iterator), JSMapIterator::kIndexOffset);
989   GotoIfNot(TaggedEqual(index, SmiConstant(0)), if_false);
990   BranchIfMapIteratorProtectorValid(&extra_checks, if_false);
991 
992   BIND(&extra_checks);
993   // Check if the iterator object has the original %MapIteratorPrototype%.
994   const TNode<NativeContext> native_context = LoadNativeContext(context);
995   const TNode<Object> initial_map_iter_proto = LoadContextElement(
996       native_context, Context::INITIAL_MAP_ITERATOR_PROTOTYPE_INDEX);
997   const TNode<HeapObject> map_iter_proto = LoadMapPrototype(iter_map);
998   GotoIfNot(TaggedEqual(map_iter_proto, initial_map_iter_proto), if_false);
999 
1000   // Check if the original MapIterator prototype has the original
1001   // %IteratorPrototype%.
1002   const TNode<Object> initial_iter_proto = LoadContextElement(
1003       native_context, Context::INITIAL_ITERATOR_PROTOTYPE_INDEX);
1004   const TNode<HeapObject> iter_proto =
1005       LoadMapPrototype(LoadMap(map_iter_proto));
1006   Branch(TaggedEqual(iter_proto, initial_iter_proto), if_true, if_false);
1007 }
1008 
BranchIfIterableWithOriginalKeyOrValueMapIterator(compiler::CodeAssemblerState * state,TNode<Object> iterable,TNode<Context> context,compiler::CodeAssemblerLabel * if_true,compiler::CodeAssemblerLabel * if_false)1009 void BranchIfIterableWithOriginalKeyOrValueMapIterator(
1010     compiler::CodeAssemblerState* state, TNode<Object> iterable,
1011     TNode<Context> context, compiler::CodeAssemblerLabel* if_true,
1012     compiler::CodeAssemblerLabel* if_false) {
1013   CollectionsBuiltinsAssembler assembler(state);
1014   assembler.BranchIfIterableWithOriginalKeyOrValueMapIterator(
1015       iterable, context, if_true, if_false);
1016 }
1017 
BranchIfSetIteratorProtectorValid(Label * if_true,Label * if_false)1018 void CollectionsBuiltinsAssembler::BranchIfSetIteratorProtectorValid(
1019     Label* if_true, Label* if_false) {
1020   const TNode<PropertyCell> protector_cell = SetIteratorProtectorConstant();
1021   DCHECK(isolate()->heap()->set_iterator_protector().IsPropertyCell());
1022   Branch(
1023       TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
1024                   SmiConstant(Protectors::kProtectorValid)),
1025       if_true, if_false);
1026 }
1027 
BranchIfIterableWithOriginalValueSetIterator(TNode<Object> iterable,TNode<Context> context,Label * if_true,Label * if_false)1028 void CollectionsBuiltinsAssembler::BranchIfIterableWithOriginalValueSetIterator(
1029     TNode<Object> iterable, TNode<Context> context, Label* if_true,
1030     Label* if_false) {
1031   Label if_set(this), if_value_iterator(this), check_protector(this);
1032   TVARIABLE(BoolT, var_result);
1033 
1034   GotoIf(TaggedIsSmi(iterable), if_false);
1035   TNode<Map> iterable_map = LoadMap(CAST(iterable));
1036   const TNode<Uint16T> instance_type = LoadMapInstanceType(iterable_map);
1037 
1038   GotoIf(InstanceTypeEqual(instance_type, JS_SET_TYPE), &if_set);
1039   Branch(InstanceTypeEqual(instance_type, JS_SET_VALUE_ITERATOR_TYPE),
1040          &if_value_iterator, if_false);
1041 
1042   BIND(&if_set);
1043   // Check if the set object has the original Set prototype.
1044   const TNode<Object> initial_set_proto = LoadContextElement(
1045       LoadNativeContext(context), Context::INITIAL_SET_PROTOTYPE_INDEX);
1046   const TNode<HeapObject> set_proto = LoadMapPrototype(iterable_map);
1047   GotoIfNot(TaggedEqual(set_proto, initial_set_proto), if_false);
1048   Goto(&check_protector);
1049 
1050   BIND(&if_value_iterator);
1051   // Check that the iterator is not partially consumed.
1052   const TNode<Object> index =
1053       LoadObjectField(CAST(iterable), JSSetIterator::kIndexOffset);
1054   GotoIfNot(TaggedEqual(index, SmiConstant(0)), if_false);
1055 
1056   // Check if the iterator object has the original SetIterator prototype.
1057   const TNode<NativeContext> native_context = LoadNativeContext(context);
1058   const TNode<Object> initial_set_iter_proto = LoadContextElement(
1059       native_context, Context::INITIAL_SET_ITERATOR_PROTOTYPE_INDEX);
1060   const TNode<HeapObject> set_iter_proto = LoadMapPrototype(iterable_map);
1061   GotoIfNot(TaggedEqual(set_iter_proto, initial_set_iter_proto), if_false);
1062 
1063   // Check if the original SetIterator prototype has the original
1064   // %IteratorPrototype%.
1065   const TNode<Object> initial_iter_proto = LoadContextElement(
1066       native_context, Context::INITIAL_ITERATOR_PROTOTYPE_INDEX);
1067   const TNode<HeapObject> iter_proto =
1068       LoadMapPrototype(LoadMap(set_iter_proto));
1069   GotoIfNot(TaggedEqual(iter_proto, initial_iter_proto), if_false);
1070   Goto(&check_protector);
1071 
1072   BIND(&check_protector);
1073   BranchIfSetIteratorProtectorValid(if_true, if_false);
1074 }
1075 
BranchIfIterableWithOriginalValueSetIterator(compiler::CodeAssemblerState * state,TNode<Object> iterable,TNode<Context> context,compiler::CodeAssemblerLabel * if_true,compiler::CodeAssemblerLabel * if_false)1076 void BranchIfIterableWithOriginalValueSetIterator(
1077     compiler::CodeAssemblerState* state, TNode<Object> iterable,
1078     TNode<Context> context, compiler::CodeAssemblerLabel* if_true,
1079     compiler::CodeAssemblerLabel* if_false) {
1080   CollectionsBuiltinsAssembler assembler(state);
1081   assembler.BranchIfIterableWithOriginalValueSetIterator(iterable, context,
1082                                                          if_true, if_false);
1083 }
1084 
MapIteratorToList(TNode<Context> context,TNode<JSMapIterator> iterator)1085 TNode<JSArray> CollectionsBuiltinsAssembler::MapIteratorToList(
1086     TNode<Context> context, TNode<JSMapIterator> iterator) {
1087   // Transition the {iterator} table if necessary.
1088   TNode<OrderedHashMap> table;
1089   TNode<IntPtrT> index;
1090   std::tie(table, index) =
1091       TransitionAndUpdate<JSMapIterator, OrderedHashMap>(iterator);
1092   CSA_DCHECK(this, IntPtrEqual(index, IntPtrConstant(0)));
1093 
1094   TNode<IntPtrT> size =
1095       LoadAndUntagObjectField(table, OrderedHashMap::NumberOfElementsOffset());
1096 
1097   const ElementsKind kind = PACKED_ELEMENTS;
1098   TNode<Map> array_map =
1099       LoadJSArrayElementsMap(kind, LoadNativeContext(context));
1100   TNode<JSArray> array =
1101       AllocateJSArray(kind, array_map, size, SmiTag(size),
1102                       AllocationFlag::kAllowLargeObjectAllocation);
1103   TNode<FixedArray> elements = CAST(LoadElements(array));
1104 
1105   const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag;
1106   TNode<IntPtrT> first_to_element_offset =
1107       ElementOffsetFromIndex(IntPtrConstant(0), kind, 0);
1108   TVARIABLE(
1109       IntPtrT, var_offset,
1110       IntPtrAdd(first_to_element_offset, IntPtrConstant(first_element_offset)));
1111   TVARIABLE(IntPtrT, var_index, index);
1112   VariableList vars({&var_index, &var_offset}, zone());
1113   Label done(this, {&var_index}), loop(this, vars), continue_loop(this, vars),
1114       write_key(this, vars), write_value(this, vars);
1115 
1116   Goto(&loop);
1117 
1118   BIND(&loop);
1119   {
1120     // Read the next entry from the {table}, skipping holes.
1121     TNode<Object> entry_key;
1122     TNode<IntPtrT> entry_start_position;
1123     TNode<IntPtrT> cur_index;
1124     std::tie(entry_key, entry_start_position, cur_index) =
1125         NextSkipHoles<OrderedHashMap>(table, var_index.value(), &done);
1126 
1127     // Decide to write key or value.
1128     Branch(
1129         InstanceTypeEqual(LoadInstanceType(iterator), JS_MAP_KEY_ITERATOR_TYPE),
1130         &write_key, &write_value);
1131 
1132     BIND(&write_key);
1133     {
1134       Store(elements, var_offset.value(), entry_key);
1135       Goto(&continue_loop);
1136     }
1137 
1138     BIND(&write_value);
1139     {
1140       CSA_DCHECK(this, InstanceTypeEqual(LoadInstanceType(iterator),
1141                                          JS_MAP_VALUE_ITERATOR_TYPE));
1142       TNode<Object> entry_value =
1143           UnsafeLoadFixedArrayElement(table, entry_start_position,
1144                                       (OrderedHashMap::HashTableStartIndex() +
1145                                        OrderedHashMap::kValueOffset) *
1146                                           kTaggedSize);
1147 
1148       Store(elements, var_offset.value(), entry_value);
1149       Goto(&continue_loop);
1150     }
1151 
1152     BIND(&continue_loop);
1153     {
1154       // Increment the array offset and continue the loop to the next entry.
1155       var_index = cur_index;
1156       var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(kTaggedSize));
1157       Goto(&loop);
1158     }
1159   }
1160 
1161   BIND(&done);
1162   // Set the {iterator} to exhausted.
1163   StoreObjectFieldRoot(iterator, JSMapIterator::kTableOffset,
1164                        RootIndex::kEmptyOrderedHashMap);
1165   StoreObjectFieldNoWriteBarrier(iterator, JSMapIterator::kIndexOffset,
1166                                  SmiTag(var_index.value()));
1167   return UncheckedCast<JSArray>(array);
1168 }
1169 
TF_BUILTIN(MapIteratorToList,CollectionsBuiltinsAssembler)1170 TF_BUILTIN(MapIteratorToList, CollectionsBuiltinsAssembler) {
1171   auto context = Parameter<Context>(Descriptor::kContext);
1172   auto iterator = Parameter<JSMapIterator>(Descriptor::kSource);
1173   Return(MapIteratorToList(context, iterator));
1174 }
1175 
SetOrSetIteratorToList(TNode<Context> context,TNode<HeapObject> iterable)1176 TNode<JSArray> CollectionsBuiltinsAssembler::SetOrSetIteratorToList(
1177     TNode<Context> context, TNode<HeapObject> iterable) {
1178   TVARIABLE(OrderedHashSet, var_table);
1179   Label if_set(this), if_iterator(this), copy(this);
1180 
1181   const TNode<Uint16T> instance_type = LoadInstanceType(iterable);
1182   Branch(InstanceTypeEqual(instance_type, JS_SET_TYPE), &if_set, &if_iterator);
1183 
1184   BIND(&if_set);
1185   {
1186     // {iterable} is a JSSet.
1187     var_table = CAST(LoadObjectField(iterable, JSSet::kTableOffset));
1188     Goto(&copy);
1189   }
1190 
1191   BIND(&if_iterator);
1192   {
1193     // {iterable} is a JSSetIterator.
1194     // Transition the {iterable} table if necessary.
1195     TNode<OrderedHashSet> iter_table;
1196     TNode<IntPtrT> iter_index;
1197     std::tie(iter_table, iter_index) =
1198         TransitionAndUpdate<JSSetIterator, OrderedHashSet>(CAST(iterable));
1199     CSA_DCHECK(this, IntPtrEqual(iter_index, IntPtrConstant(0)));
1200     var_table = iter_table;
1201     Goto(&copy);
1202   }
1203 
1204   BIND(&copy);
1205   TNode<OrderedHashSet> table = var_table.value();
1206   TNode<IntPtrT> size =
1207       LoadAndUntagObjectField(table, OrderedHashMap::NumberOfElementsOffset());
1208 
1209   const ElementsKind kind = PACKED_ELEMENTS;
1210   TNode<Map> array_map =
1211       LoadJSArrayElementsMap(kind, LoadNativeContext(context));
1212   TNode<JSArray> array =
1213       AllocateJSArray(kind, array_map, size, SmiTag(size),
1214                       AllocationFlag::kAllowLargeObjectAllocation);
1215   TNode<FixedArray> elements = CAST(LoadElements(array));
1216 
1217   const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag;
1218   TNode<IntPtrT> first_to_element_offset =
1219       ElementOffsetFromIndex(IntPtrConstant(0), kind, 0);
1220   TVARIABLE(
1221       IntPtrT, var_offset,
1222       IntPtrAdd(first_to_element_offset, IntPtrConstant(first_element_offset)));
1223   TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
1224   Label done(this), finalize(this, {&var_index}),
1225       loop(this, {&var_index, &var_offset});
1226 
1227   Goto(&loop);
1228 
1229   BIND(&loop);
1230   {
1231     // Read the next entry from the {table}, skipping holes.
1232     TNode<Object> entry_key;
1233     TNode<IntPtrT> entry_start_position;
1234     TNode<IntPtrT> cur_index;
1235     std::tie(entry_key, entry_start_position, cur_index) =
1236         NextSkipHoles<OrderedHashSet>(table, var_index.value(), &finalize);
1237 
1238     Store(elements, var_offset.value(), entry_key);
1239 
1240     var_index = cur_index;
1241     var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(kTaggedSize));
1242     Goto(&loop);
1243   }
1244 
1245   BIND(&finalize);
1246   GotoIf(InstanceTypeEqual(instance_type, JS_SET_TYPE), &done);
1247   // Set the {iterable} to exhausted if it's an iterator.
1248   StoreObjectFieldRoot(iterable, JSSetIterator::kTableOffset,
1249                        RootIndex::kEmptyOrderedHashSet);
1250   StoreObjectFieldNoWriteBarrier(iterable, JSSetIterator::kIndexOffset,
1251                                  SmiTag(var_index.value()));
1252   Goto(&done);
1253 
1254   BIND(&done);
1255   return UncheckedCast<JSArray>(array);
1256 }
1257 
TF_BUILTIN(SetOrSetIteratorToList,CollectionsBuiltinsAssembler)1258 TF_BUILTIN(SetOrSetIteratorToList, CollectionsBuiltinsAssembler) {
1259   auto context = Parameter<Context>(Descriptor::kContext);
1260   auto object = Parameter<HeapObject>(Descriptor::kSource);
1261   Return(SetOrSetIteratorToList(context, object));
1262 }
1263 
ComputeUnseededHash(TNode<IntPtrT> key)1264 TNode<Word32T> CollectionsBuiltinsAssembler::ComputeUnseededHash(
1265     TNode<IntPtrT> key) {
1266   // See v8::internal::ComputeUnseededHash()
1267   TNode<Word32T> hash = TruncateIntPtrToInt32(key);
1268   hash = Int32Add(Word32Xor(hash, Int32Constant(0xFFFFFFFF)),
1269                   Word32Shl(hash, Int32Constant(15)));
1270   hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(12)));
1271   hash = Int32Add(hash, Word32Shl(hash, Int32Constant(2)));
1272   hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(4)));
1273   hash = Int32Mul(hash, Int32Constant(2057));
1274   hash = Word32Xor(hash, Word32Shr(hash, Int32Constant(16)));
1275   return Word32And(hash, Int32Constant(0x3FFFFFFF));
1276 }
1277 
1278 template <typename CollectionType>
FindOrderedHashTableEntryForSmiKey(TNode<CollectionType> table,TNode<Smi> smi_key,TVariable<IntPtrT> * result,Label * entry_found,Label * not_found)1279 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForSmiKey(
1280     TNode<CollectionType> table, TNode<Smi> smi_key, TVariable<IntPtrT>* result,
1281     Label* entry_found, Label* not_found) {
1282   const TNode<IntPtrT> key_untagged = SmiUntag(smi_key);
1283   const TNode<IntPtrT> hash =
1284       ChangeInt32ToIntPtr(ComputeUnseededHash(key_untagged));
1285   CSA_DCHECK(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
1286   *result = hash;
1287   FindOrderedHashTableEntry<CollectionType>(
1288       table, hash,
1289       [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1290         SameValueZeroSmi(smi_key, other_key, if_same, if_not_same);
1291       },
1292       result, entry_found, not_found);
1293 }
1294 
1295 template <typename CollectionType>
FindOrderedHashTableEntryForStringKey(TNode<CollectionType> table,TNode<String> key_tagged,TVariable<IntPtrT> * result,Label * entry_found,Label * not_found)1296 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForStringKey(
1297     TNode<CollectionType> table, TNode<String> key_tagged,
1298     TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1299   const TNode<IntPtrT> hash = ComputeStringHash(key_tagged);
1300   CSA_DCHECK(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
1301   *result = hash;
1302   FindOrderedHashTableEntry<CollectionType>(
1303       table, hash,
1304       [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1305         SameValueZeroString(key_tagged, other_key, if_same, if_not_same);
1306       },
1307       result, entry_found, not_found);
1308 }
1309 
1310 template <typename CollectionType>
FindOrderedHashTableEntryForHeapNumberKey(TNode<CollectionType> table,TNode<HeapNumber> key_heap_number,TVariable<IntPtrT> * result,Label * entry_found,Label * not_found)1311 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForHeapNumberKey(
1312     TNode<CollectionType> table, TNode<HeapNumber> key_heap_number,
1313     TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1314   const TNode<IntPtrT> hash = CallGetHashRaw(key_heap_number);
1315   CSA_DCHECK(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
1316   *result = hash;
1317   const TNode<Float64T> key_float = LoadHeapNumberValue(key_heap_number);
1318   FindOrderedHashTableEntry<CollectionType>(
1319       table, hash,
1320       [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1321         SameValueZeroHeapNumber(key_float, other_key, if_same, if_not_same);
1322       },
1323       result, entry_found, not_found);
1324 }
1325 
1326 template <typename CollectionType>
FindOrderedHashTableEntryForBigIntKey(TNode<CollectionType> table,TNode<BigInt> key_big_int,TVariable<IntPtrT> * result,Label * entry_found,Label * not_found)1327 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForBigIntKey(
1328     TNode<CollectionType> table, TNode<BigInt> key_big_int,
1329     TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1330   const TNode<IntPtrT> hash = CallGetHashRaw(key_big_int);
1331   CSA_DCHECK(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
1332   *result = hash;
1333   FindOrderedHashTableEntry<CollectionType>(
1334       table, hash,
1335       [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1336         SameValueZeroBigInt(key_big_int, other_key, if_same, if_not_same);
1337       },
1338       result, entry_found, not_found);
1339 }
1340 
1341 template <typename CollectionType>
FindOrderedHashTableEntryForOtherKey(TNode<CollectionType> table,TNode<HeapObject> key_heap_object,TVariable<IntPtrT> * result,Label * entry_found,Label * not_found)1342 void CollectionsBuiltinsAssembler::FindOrderedHashTableEntryForOtherKey(
1343     TNode<CollectionType> table, TNode<HeapObject> key_heap_object,
1344     TVariable<IntPtrT>* result, Label* entry_found, Label* not_found) {
1345   const TNode<IntPtrT> hash = GetHash(key_heap_object);
1346   CSA_DCHECK(this, IntPtrGreaterThanOrEqual(hash, IntPtrConstant(0)));
1347   *result = hash;
1348   FindOrderedHashTableEntry<CollectionType>(
1349       table, hash,
1350       [&](TNode<Object> other_key, Label* if_same, Label* if_not_same) {
1351         Branch(TaggedEqual(key_heap_object, other_key), if_same, if_not_same);
1352       },
1353       result, entry_found, not_found);
1354 }
1355 
ComputeStringHash(TNode<String> string_key)1356 TNode<IntPtrT> CollectionsBuiltinsAssembler::ComputeStringHash(
1357     TNode<String> string_key) {
1358   TVARIABLE(IntPtrT, var_result);
1359 
1360   Label hash_not_computed(this), done(this, &var_result);
1361   const TNode<IntPtrT> hash =
1362       ChangeInt32ToIntPtr(LoadNameHash(string_key, &hash_not_computed));
1363   var_result = hash;
1364   Goto(&done);
1365 
1366   BIND(&hash_not_computed);
1367   var_result = CallGetHashRaw(string_key);
1368   Goto(&done);
1369 
1370   BIND(&done);
1371   return var_result.value();
1372 }
1373 
SameValueZeroString(TNode<String> key_string,TNode<Object> candidate_key,Label * if_same,Label * if_not_same)1374 void CollectionsBuiltinsAssembler::SameValueZeroString(
1375     TNode<String> key_string, TNode<Object> candidate_key, Label* if_same,
1376     Label* if_not_same) {
1377   // If the candidate is not a string, the keys are not equal.
1378   GotoIf(TaggedIsSmi(candidate_key), if_not_same);
1379   GotoIfNot(IsString(CAST(candidate_key)), if_not_same);
1380 
1381   Branch(TaggedEqual(CallBuiltin(Builtin::kStringEqual, NoContextConstant(),
1382                                  key_string, candidate_key),
1383                      TrueConstant()),
1384          if_same, if_not_same);
1385 }
1386 
SameValueZeroBigInt(TNode<BigInt> key,TNode<Object> candidate_key,Label * if_same,Label * if_not_same)1387 void CollectionsBuiltinsAssembler::SameValueZeroBigInt(
1388     TNode<BigInt> key, TNode<Object> candidate_key, Label* if_same,
1389     Label* if_not_same) {
1390   GotoIf(TaggedIsSmi(candidate_key), if_not_same);
1391   GotoIfNot(IsBigInt(CAST(candidate_key)), if_not_same);
1392 
1393   Branch(TaggedEqual(CallRuntime(Runtime::kBigIntEqualToBigInt,
1394                                  NoContextConstant(), key, candidate_key),
1395                      TrueConstant()),
1396          if_same, if_not_same);
1397 }
1398 
SameValueZeroHeapNumber(TNode<Float64T> key_float,TNode<Object> candidate_key,Label * if_same,Label * if_not_same)1399 void CollectionsBuiltinsAssembler::SameValueZeroHeapNumber(
1400     TNode<Float64T> key_float, TNode<Object> candidate_key, Label* if_same,
1401     Label* if_not_same) {
1402   Label if_smi(this), if_keyisnan(this);
1403 
1404   GotoIf(TaggedIsSmi(candidate_key), &if_smi);
1405   GotoIfNot(IsHeapNumber(CAST(candidate_key)), if_not_same);
1406 
1407   {
1408     // {candidate_key} is a heap number.
1409     const TNode<Float64T> candidate_float =
1410         LoadHeapNumberValue(CAST(candidate_key));
1411     GotoIf(Float64Equal(key_float, candidate_float), if_same);
1412 
1413     // SameValueZero needs to treat NaNs as equal. First check if {key_float}
1414     // is NaN.
1415     BranchIfFloat64IsNaN(key_float, &if_keyisnan, if_not_same);
1416 
1417     BIND(&if_keyisnan);
1418     {
1419       // Return true iff {candidate_key} is NaN.
1420       Branch(Float64Equal(candidate_float, candidate_float), if_not_same,
1421              if_same);
1422     }
1423   }
1424 
1425   BIND(&if_smi);
1426   {
1427     const TNode<Float64T> candidate_float = SmiToFloat64(CAST(candidate_key));
1428     Branch(Float64Equal(key_float, candidate_float), if_same, if_not_same);
1429   }
1430 }
1431 
TF_BUILTIN(OrderedHashTableHealIndex,CollectionsBuiltinsAssembler)1432 TF_BUILTIN(OrderedHashTableHealIndex, CollectionsBuiltinsAssembler) {
1433   auto table = Parameter<HeapObject>(Descriptor::kTable);
1434   auto index = Parameter<Smi>(Descriptor::kIndex);
1435   Label return_index(this), return_zero(this);
1436 
1437   // Check if we need to update the {index}.
1438   GotoIfNot(SmiLessThan(SmiConstant(0), index), &return_zero);
1439 
1440   // Check if the {table} was cleared.
1441   STATIC_ASSERT(OrderedHashMap::NumberOfDeletedElementsOffset() ==
1442                 OrderedHashSet::NumberOfDeletedElementsOffset());
1443   TNode<IntPtrT> number_of_deleted_elements = LoadAndUntagObjectField(
1444       table, OrderedHashMap::NumberOfDeletedElementsOffset());
1445   STATIC_ASSERT(OrderedHashMap::kClearedTableSentinel ==
1446                 OrderedHashSet::kClearedTableSentinel);
1447   GotoIf(IntPtrEqual(number_of_deleted_elements,
1448                      IntPtrConstant(OrderedHashMap::kClearedTableSentinel)),
1449          &return_zero);
1450 
1451   TVARIABLE(IntPtrT, var_i, IntPtrConstant(0));
1452   TVARIABLE(Smi, var_index, index);
1453   Label loop(this, {&var_i, &var_index});
1454   Goto(&loop);
1455   BIND(&loop);
1456   {
1457     TNode<IntPtrT> i = var_i.value();
1458     GotoIfNot(IntPtrLessThan(i, number_of_deleted_elements), &return_index);
1459     STATIC_ASSERT(OrderedHashMap::RemovedHolesIndex() ==
1460                   OrderedHashSet::RemovedHolesIndex());
1461     TNode<Smi> removed_index = CAST(LoadFixedArrayElement(
1462         CAST(table), i, OrderedHashMap::RemovedHolesIndex() * kTaggedSize));
1463     GotoIf(SmiGreaterThanOrEqual(removed_index, index), &return_index);
1464     Decrement(&var_index);
1465     Increment(&var_i);
1466     Goto(&loop);
1467   }
1468 
1469   BIND(&return_index);
1470   Return(var_index.value());
1471 
1472   BIND(&return_zero);
1473   Return(SmiConstant(0));
1474 }
1475 
1476 template <typename TableType>
1477 std::pair<TNode<TableType>, TNode<IntPtrT>>
Transition(const TNode<TableType> table,const TNode<IntPtrT> index,UpdateInTransition<TableType> const & update_in_transition)1478 CollectionsBuiltinsAssembler::Transition(
1479     const TNode<TableType> table, const TNode<IntPtrT> index,
1480     UpdateInTransition<TableType> const& update_in_transition) {
1481   TVARIABLE(IntPtrT, var_index, index);
1482   TVARIABLE(TableType, var_table, table);
1483   Label if_done(this), if_transition(this, Label::kDeferred);
1484   Branch(TaggedIsSmi(
1485              LoadObjectField(var_table.value(), TableType::NextTableOffset())),
1486          &if_done, &if_transition);
1487 
1488   BIND(&if_transition);
1489   {
1490     Label loop(this, {&var_table, &var_index}), done_loop(this);
1491     Goto(&loop);
1492     BIND(&loop);
1493     {
1494       TNode<TableType> current_table = var_table.value();
1495       TNode<IntPtrT> current_index = var_index.value();
1496 
1497       TNode<Object> next_table =
1498           LoadObjectField(current_table, TableType::NextTableOffset());
1499       GotoIf(TaggedIsSmi(next_table), &done_loop);
1500 
1501       var_table = CAST(next_table);
1502       var_index = SmiUntag(CAST(CallBuiltin(Builtin::kOrderedHashTableHealIndex,
1503                                             NoContextConstant(), current_table,
1504                                             SmiTag(current_index))));
1505       Goto(&loop);
1506     }
1507     BIND(&done_loop);
1508 
1509     // Update with the new {table} and {index}.
1510     update_in_transition(var_table.value(), var_index.value());
1511     Goto(&if_done);
1512   }
1513 
1514   BIND(&if_done);
1515   return {var_table.value(), var_index.value()};
1516 }
1517 
1518 template <typename IteratorType, typename TableType>
1519 std::pair<TNode<TableType>, TNode<IntPtrT>>
TransitionAndUpdate(const TNode<IteratorType> iterator)1520 CollectionsBuiltinsAssembler::TransitionAndUpdate(
1521     const TNode<IteratorType> iterator) {
1522   return Transition<TableType>(
1523       CAST(LoadObjectField(iterator, IteratorType::kTableOffset)),
1524       LoadAndUntagObjectField(iterator, IteratorType::kIndexOffset),
1525       [this, iterator](const TNode<TableType> table,
1526                        const TNode<IntPtrT> index) {
1527         // Update the {iterator} with the new state.
1528         StoreObjectField(iterator, IteratorType::kTableOffset, table);
1529         StoreObjectFieldNoWriteBarrier(iterator, IteratorType::kIndexOffset,
1530                                        SmiTag(index));
1531       });
1532 }
1533 
1534 template <typename TableType>
1535 std::tuple<TNode<Object>, TNode<IntPtrT>, TNode<IntPtrT>>
NextSkipHoles(TNode<TableType> table,TNode<IntPtrT> index,Label * if_end)1536 CollectionsBuiltinsAssembler::NextSkipHoles(TNode<TableType> table,
1537                                             TNode<IntPtrT> index,
1538                                             Label* if_end) {
1539   // Compute the used capacity for the {table}.
1540   TNode<IntPtrT> number_of_buckets =
1541       LoadAndUntagObjectField(table, TableType::NumberOfBucketsOffset());
1542   TNode<IntPtrT> number_of_elements =
1543       LoadAndUntagObjectField(table, TableType::NumberOfElementsOffset());
1544   TNode<IntPtrT> number_of_deleted_elements = LoadAndUntagObjectField(
1545       table, TableType::NumberOfDeletedElementsOffset());
1546   TNode<IntPtrT> used_capacity =
1547       IntPtrAdd(number_of_elements, number_of_deleted_elements);
1548 
1549   TNode<Object> entry_key;
1550   TNode<IntPtrT> entry_start_position;
1551   TVARIABLE(IntPtrT, var_index, index);
1552   Label loop(this, &var_index), done_loop(this);
1553   Goto(&loop);
1554   BIND(&loop);
1555   {
1556     GotoIfNot(IntPtrLessThan(var_index.value(), used_capacity), if_end);
1557     entry_start_position = IntPtrAdd(
1558         IntPtrMul(var_index.value(), IntPtrConstant(TableType::kEntrySize)),
1559         number_of_buckets);
1560     entry_key = UnsafeLoadFixedArrayElement(
1561         table, entry_start_position,
1562         TableType::HashTableStartIndex() * kTaggedSize);
1563     Increment(&var_index);
1564     Branch(IsTheHole(entry_key), &loop, &done_loop);
1565   }
1566 
1567   BIND(&done_loop);
1568   return std::tuple<TNode<Object>, TNode<IntPtrT>, TNode<IntPtrT>>{
1569       entry_key, entry_start_position, var_index.value()};
1570 }
1571 
TF_BUILTIN(MapPrototypeGet,CollectionsBuiltinsAssembler)1572 TF_BUILTIN(MapPrototypeGet, CollectionsBuiltinsAssembler) {
1573   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1574   const auto key = Parameter<Object>(Descriptor::kKey);
1575   const auto context = Parameter<Context>(Descriptor::kContext);
1576 
1577   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.get");
1578 
1579   const TNode<Object> table =
1580       LoadObjectField<Object>(CAST(receiver), JSMap::kTableOffset);
1581   TNode<Smi> index =
1582       CAST(CallBuiltin(Builtin::kFindOrderedHashMapEntry, context, table, key));
1583 
1584   Label if_found(this), if_not_found(this);
1585   Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found,
1586          &if_not_found);
1587 
1588   BIND(&if_found);
1589   Return(LoadFixedArrayElement(
1590       CAST(table), SmiUntag(index),
1591       (OrderedHashMap::HashTableStartIndex() + OrderedHashMap::kValueOffset) *
1592           kTaggedSize));
1593 
1594   BIND(&if_not_found);
1595   Return(UndefinedConstant());
1596 }
1597 
TF_BUILTIN(MapPrototypeHas,CollectionsBuiltinsAssembler)1598 TF_BUILTIN(MapPrototypeHas, CollectionsBuiltinsAssembler) {
1599   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1600   const auto key = Parameter<Object>(Descriptor::kKey);
1601   const auto context = Parameter<Context>(Descriptor::kContext);
1602 
1603   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.has");
1604 
1605   const TNode<Object> table =
1606       LoadObjectField(CAST(receiver), JSMap::kTableOffset);
1607   TNode<Smi> index =
1608       CAST(CallBuiltin(Builtin::kFindOrderedHashMapEntry, context, table, key));
1609 
1610   Label if_found(this), if_not_found(this);
1611   Branch(SmiGreaterThanOrEqual(index, SmiConstant(0)), &if_found,
1612          &if_not_found);
1613 
1614   BIND(&if_found);
1615   Return(TrueConstant());
1616 
1617   BIND(&if_not_found);
1618   Return(FalseConstant());
1619 }
1620 
NormalizeNumberKey(const TNode<Object> key)1621 const TNode<Object> CollectionsBuiltinsAssembler::NormalizeNumberKey(
1622     const TNode<Object> key) {
1623   TVARIABLE(Object, result, key);
1624   Label done(this);
1625 
1626   GotoIf(TaggedIsSmi(key), &done);
1627   GotoIfNot(IsHeapNumber(CAST(key)), &done);
1628   const TNode<Float64T> number = LoadHeapNumberValue(CAST(key));
1629   GotoIfNot(Float64Equal(number, Float64Constant(0.0)), &done);
1630   // We know the value is zero, so we take the key to be Smi 0.
1631   // Another option would be to normalize to Smi here.
1632   result = SmiConstant(0);
1633   Goto(&done);
1634 
1635   BIND(&done);
1636   return result.value();
1637 }
1638 
TF_BUILTIN(MapPrototypeSet,CollectionsBuiltinsAssembler)1639 TF_BUILTIN(MapPrototypeSet, CollectionsBuiltinsAssembler) {
1640   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1641   auto key = Parameter<Object>(Descriptor::kKey);
1642   const auto value = Parameter<Object>(Descriptor::kValue);
1643   const auto context = Parameter<Context>(Descriptor::kContext);
1644 
1645   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.set");
1646 
1647   key = NormalizeNumberKey(key);
1648 
1649   const TNode<OrderedHashMap> table =
1650       LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1651 
1652   TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
1653   Label entry_found(this), not_found(this);
1654 
1655   TryLookupOrderedHashTableIndex<OrderedHashMap>(
1656       table, key, &entry_start_position_or_hash, &entry_found, &not_found);
1657 
1658   BIND(&entry_found);
1659   // If we found the entry, we just store the value there.
1660   StoreFixedArrayElement(table, entry_start_position_or_hash.value(), value,
1661                          UPDATE_WRITE_BARRIER,
1662                          kTaggedSize * (OrderedHashMap::HashTableStartIndex() +
1663                                         OrderedHashMap::kValueOffset));
1664   Return(receiver);
1665 
1666   Label no_hash(this), add_entry(this), store_new_entry(this);
1667   BIND(&not_found);
1668   {
1669     // If we have a hash code, we can start adding the new entry.
1670     GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(),
1671                              IntPtrConstant(0)),
1672            &add_entry);
1673 
1674     // Otherwise, go to runtime to compute the hash code.
1675     entry_start_position_or_hash = SmiUntag(CallGetOrCreateHashRaw(CAST(key)));
1676     Goto(&add_entry);
1677   }
1678 
1679   BIND(&add_entry);
1680   TVARIABLE(IntPtrT, number_of_buckets);
1681   TVARIABLE(IntPtrT, occupancy);
1682   TVARIABLE(OrderedHashMap, table_var, table);
1683   {
1684     // Check we have enough space for the entry.
1685     number_of_buckets = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
1686         table, OrderedHashMap::NumberOfBucketsIndex())));
1687 
1688     STATIC_ASSERT(OrderedHashMap::kLoadFactor == 2);
1689     const TNode<WordT> capacity = WordShl(number_of_buckets.value(), 1);
1690     const TNode<IntPtrT> number_of_elements = SmiUntag(
1691         CAST(LoadObjectField(table, OrderedHashMap::NumberOfElementsOffset())));
1692     const TNode<IntPtrT> number_of_deleted = SmiUntag(CAST(LoadObjectField(
1693         table, OrderedHashMap::NumberOfDeletedElementsOffset())));
1694     occupancy = IntPtrAdd(number_of_elements, number_of_deleted);
1695     GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry);
1696 
1697     // We do not have enough space, grow the table and reload the relevant
1698     // fields.
1699     CallRuntime(Runtime::kMapGrow, context, receiver);
1700     table_var =
1701         LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1702     number_of_buckets = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
1703         table_var.value(), OrderedHashMap::NumberOfBucketsIndex())));
1704     const TNode<IntPtrT> new_number_of_elements = SmiUntag(CAST(LoadObjectField(
1705         table_var.value(), OrderedHashMap::NumberOfElementsOffset())));
1706     const TNode<IntPtrT> new_number_of_deleted = SmiUntag(CAST(LoadObjectField(
1707         table_var.value(), OrderedHashMap::NumberOfDeletedElementsOffset())));
1708     occupancy = IntPtrAdd(new_number_of_elements, new_number_of_deleted);
1709     Goto(&store_new_entry);
1710   }
1711   BIND(&store_new_entry);
1712   // Store the key, value and connect the element to the bucket chain.
1713   StoreOrderedHashMapNewEntry(table_var.value(), key, value,
1714                               entry_start_position_or_hash.value(),
1715                               number_of_buckets.value(), occupancy.value());
1716   Return(receiver);
1717 }
1718 
StoreOrderedHashMapNewEntry(const TNode<OrderedHashMap> table,const TNode<Object> key,const TNode<Object> value,const TNode<IntPtrT> hash,const TNode<IntPtrT> number_of_buckets,const TNode<IntPtrT> occupancy)1719 void CollectionsBuiltinsAssembler::StoreOrderedHashMapNewEntry(
1720     const TNode<OrderedHashMap> table, const TNode<Object> key,
1721     const TNode<Object> value, const TNode<IntPtrT> hash,
1722     const TNode<IntPtrT> number_of_buckets, const TNode<IntPtrT> occupancy) {
1723   const TNode<IntPtrT> bucket =
1724       WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1)));
1725   TNode<Smi> bucket_entry = CAST(UnsafeLoadFixedArrayElement(
1726       table, bucket, OrderedHashMap::HashTableStartIndex() * kTaggedSize));
1727 
1728   // Store the entry elements.
1729   const TNode<IntPtrT> entry_start = IntPtrAdd(
1730       IntPtrMul(occupancy, IntPtrConstant(OrderedHashMap::kEntrySize)),
1731       number_of_buckets);
1732   UnsafeStoreFixedArrayElement(
1733       table, entry_start, key, UPDATE_WRITE_BARRIER,
1734       kTaggedSize * OrderedHashMap::HashTableStartIndex());
1735   UnsafeStoreFixedArrayElement(
1736       table, entry_start, value, UPDATE_WRITE_BARRIER,
1737       kTaggedSize * (OrderedHashMap::HashTableStartIndex() +
1738                      OrderedHashMap::kValueOffset));
1739   UnsafeStoreFixedArrayElement(
1740       table, entry_start, bucket_entry,
1741       kTaggedSize * (OrderedHashMap::HashTableStartIndex() +
1742                      OrderedHashMap::kChainOffset));
1743 
1744   // Update the bucket head.
1745   UnsafeStoreFixedArrayElement(
1746       table, bucket, SmiTag(occupancy),
1747       OrderedHashMap::HashTableStartIndex() * kTaggedSize);
1748 
1749   // Bump the elements count.
1750   const TNode<Smi> number_of_elements =
1751       CAST(LoadObjectField(table, OrderedHashMap::NumberOfElementsOffset()));
1752   StoreObjectFieldNoWriteBarrier(table,
1753                                  OrderedHashMap::NumberOfElementsOffset(),
1754                                  SmiAdd(number_of_elements, SmiConstant(1)));
1755 }
1756 
TF_BUILTIN(MapPrototypeDelete,CollectionsBuiltinsAssembler)1757 TF_BUILTIN(MapPrototypeDelete, CollectionsBuiltinsAssembler) {
1758   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1759   const auto key = Parameter<Object>(Descriptor::kKey);
1760   const auto context = Parameter<Context>(Descriptor::kContext);
1761 
1762   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1763                          "Map.prototype.delete");
1764 
1765   const TNode<OrderedHashMap> table =
1766       LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1767 
1768   TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
1769   Label entry_found(this), not_found(this);
1770 
1771   TryLookupOrderedHashTableIndex<OrderedHashMap>(
1772       table, key, &entry_start_position_or_hash, &entry_found, &not_found);
1773 
1774   BIND(&not_found);
1775   Return(FalseConstant());
1776 
1777   BIND(&entry_found);
1778   // If we found the entry, mark the entry as deleted.
1779   StoreFixedArrayElement(table, entry_start_position_or_hash.value(),
1780                          TheHoleConstant(), UPDATE_WRITE_BARRIER,
1781                          kTaggedSize * OrderedHashMap::HashTableStartIndex());
1782   StoreFixedArrayElement(table, entry_start_position_or_hash.value(),
1783                          TheHoleConstant(), UPDATE_WRITE_BARRIER,
1784                          kTaggedSize * (OrderedHashMap::HashTableStartIndex() +
1785                                         OrderedHashMap::kValueOffset));
1786 
1787   // Decrement the number of elements, increment the number of deleted elements.
1788   const TNode<Smi> number_of_elements = SmiSub(
1789       CAST(LoadObjectField(table, OrderedHashMap::NumberOfElementsOffset())),
1790       SmiConstant(1));
1791   StoreObjectFieldNoWriteBarrier(
1792       table, OrderedHashMap::NumberOfElementsOffset(), number_of_elements);
1793   const TNode<Smi> number_of_deleted =
1794       SmiAdd(CAST(LoadObjectField(
1795                  table, OrderedHashMap::NumberOfDeletedElementsOffset())),
1796              SmiConstant(1));
1797   StoreObjectFieldNoWriteBarrier(
1798       table, OrderedHashMap::NumberOfDeletedElementsOffset(),
1799       number_of_deleted);
1800 
1801   const TNode<Smi> number_of_buckets = CAST(
1802       LoadFixedArrayElement(table, OrderedHashMap::NumberOfBucketsIndex()));
1803 
1804   // If there fewer elements than #buckets / 2, shrink the table.
1805   Label shrink(this);
1806   GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements),
1807                      number_of_buckets),
1808          &shrink);
1809   Return(TrueConstant());
1810 
1811   BIND(&shrink);
1812   CallRuntime(Runtime::kMapShrink, context, receiver);
1813   Return(TrueConstant());
1814 }
1815 
TF_BUILTIN(SetPrototypeAdd,CollectionsBuiltinsAssembler)1816 TF_BUILTIN(SetPrototypeAdd, CollectionsBuiltinsAssembler) {
1817   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1818   auto key = Parameter<Object>(Descriptor::kKey);
1819   const auto context = Parameter<Context>(Descriptor::kContext);
1820 
1821   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.add");
1822 
1823   key = NormalizeNumberKey(key);
1824 
1825   const TNode<OrderedHashSet> table =
1826       LoadObjectField<OrderedHashSet>(CAST(receiver), JSMap::kTableOffset);
1827 
1828   TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
1829   Label entry_found(this), not_found(this);
1830 
1831   TryLookupOrderedHashTableIndex<OrderedHashSet>(
1832       table, key, &entry_start_position_or_hash, &entry_found, &not_found);
1833 
1834   BIND(&entry_found);
1835   // The entry was found, there is nothing to do.
1836   Return(receiver);
1837 
1838   Label no_hash(this), add_entry(this), store_new_entry(this);
1839   BIND(&not_found);
1840   {
1841     // If we have a hash code, we can start adding the new entry.
1842     GotoIf(IntPtrGreaterThan(entry_start_position_or_hash.value(),
1843                              IntPtrConstant(0)),
1844            &add_entry);
1845 
1846     // Otherwise, go to runtime to compute the hash code.
1847     entry_start_position_or_hash = SmiUntag(CallGetOrCreateHashRaw(CAST(key)));
1848     Goto(&add_entry);
1849   }
1850 
1851   BIND(&add_entry);
1852   TVARIABLE(IntPtrT, number_of_buckets);
1853   TVARIABLE(IntPtrT, occupancy);
1854   TVARIABLE(OrderedHashSet, table_var, table);
1855   {
1856     // Check we have enough space for the entry.
1857     number_of_buckets = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
1858         table, OrderedHashSet::NumberOfBucketsIndex())));
1859 
1860     STATIC_ASSERT(OrderedHashSet::kLoadFactor == 2);
1861     const TNode<WordT> capacity = WordShl(number_of_buckets.value(), 1);
1862     const TNode<IntPtrT> number_of_elements = SmiUntag(
1863         CAST(LoadObjectField(table, OrderedHashSet::NumberOfElementsOffset())));
1864     const TNode<IntPtrT> number_of_deleted = SmiUntag(CAST(LoadObjectField(
1865         table, OrderedHashSet::NumberOfDeletedElementsOffset())));
1866     occupancy = IntPtrAdd(number_of_elements, number_of_deleted);
1867     GotoIf(IntPtrLessThan(occupancy.value(), capacity), &store_new_entry);
1868 
1869     // We do not have enough space, grow the table and reload the relevant
1870     // fields.
1871     CallRuntime(Runtime::kSetGrow, context, receiver);
1872     table_var =
1873         LoadObjectField<OrderedHashSet>(CAST(receiver), JSMap::kTableOffset);
1874     number_of_buckets = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
1875         table_var.value(), OrderedHashSet::NumberOfBucketsIndex())));
1876     const TNode<IntPtrT> new_number_of_elements = SmiUntag(CAST(LoadObjectField(
1877         table_var.value(), OrderedHashSet::NumberOfElementsOffset())));
1878     const TNode<IntPtrT> new_number_of_deleted = SmiUntag(CAST(LoadObjectField(
1879         table_var.value(), OrderedHashSet::NumberOfDeletedElementsOffset())));
1880     occupancy = IntPtrAdd(new_number_of_elements, new_number_of_deleted);
1881     Goto(&store_new_entry);
1882   }
1883   BIND(&store_new_entry);
1884   // Store the key, value and connect the element to the bucket chain.
1885   StoreOrderedHashSetNewEntry(table_var.value(), key,
1886                               entry_start_position_or_hash.value(),
1887                               number_of_buckets.value(), occupancy.value());
1888   Return(receiver);
1889 }
1890 
StoreOrderedHashSetNewEntry(const TNode<OrderedHashSet> table,const TNode<Object> key,const TNode<IntPtrT> hash,const TNode<IntPtrT> number_of_buckets,const TNode<IntPtrT> occupancy)1891 void CollectionsBuiltinsAssembler::StoreOrderedHashSetNewEntry(
1892     const TNode<OrderedHashSet> table, const TNode<Object> key,
1893     const TNode<IntPtrT> hash, const TNode<IntPtrT> number_of_buckets,
1894     const TNode<IntPtrT> occupancy) {
1895   const TNode<IntPtrT> bucket =
1896       WordAnd(hash, IntPtrSub(number_of_buckets, IntPtrConstant(1)));
1897   TNode<Smi> bucket_entry = CAST(UnsafeLoadFixedArrayElement(
1898       table, bucket, OrderedHashSet::HashTableStartIndex() * kTaggedSize));
1899 
1900   // Store the entry elements.
1901   const TNode<IntPtrT> entry_start = IntPtrAdd(
1902       IntPtrMul(occupancy, IntPtrConstant(OrderedHashSet::kEntrySize)),
1903       number_of_buckets);
1904   UnsafeStoreFixedArrayElement(
1905       table, entry_start, key, UPDATE_WRITE_BARRIER,
1906       kTaggedSize * OrderedHashSet::HashTableStartIndex());
1907   UnsafeStoreFixedArrayElement(
1908       table, entry_start, bucket_entry,
1909       kTaggedSize * (OrderedHashSet::HashTableStartIndex() +
1910                      OrderedHashSet::kChainOffset));
1911 
1912   // Update the bucket head.
1913   UnsafeStoreFixedArrayElement(
1914       table, bucket, SmiTag(occupancy),
1915       OrderedHashSet::HashTableStartIndex() * kTaggedSize);
1916 
1917   // Bump the elements count.
1918   const TNode<Smi> number_of_elements =
1919       CAST(LoadObjectField(table, OrderedHashSet::NumberOfElementsOffset()));
1920   StoreObjectFieldNoWriteBarrier(table,
1921                                  OrderedHashSet::NumberOfElementsOffset(),
1922                                  SmiAdd(number_of_elements, SmiConstant(1)));
1923 }
1924 
TF_BUILTIN(SetPrototypeDelete,CollectionsBuiltinsAssembler)1925 TF_BUILTIN(SetPrototypeDelete, CollectionsBuiltinsAssembler) {
1926   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1927   const auto key = Parameter<Object>(Descriptor::kKey);
1928   const auto context = Parameter<Context>(Descriptor::kContext);
1929 
1930   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
1931                          "Set.prototype.delete");
1932 
1933   const TNode<OrderedHashSet> table =
1934       LoadObjectField<OrderedHashSet>(CAST(receiver), JSMap::kTableOffset);
1935 
1936   TVARIABLE(IntPtrT, entry_start_position_or_hash, IntPtrConstant(0));
1937   Label entry_found(this), not_found(this);
1938 
1939   TryLookupOrderedHashTableIndex<OrderedHashSet>(
1940       table, key, &entry_start_position_or_hash, &entry_found, &not_found);
1941 
1942   BIND(&not_found);
1943   Return(FalseConstant());
1944 
1945   BIND(&entry_found);
1946   // If we found the entry, mark the entry as deleted.
1947   StoreFixedArrayElement(table, entry_start_position_or_hash.value(),
1948                          TheHoleConstant(), UPDATE_WRITE_BARRIER,
1949                          kTaggedSize * OrderedHashSet::HashTableStartIndex());
1950 
1951   // Decrement the number of elements, increment the number of deleted elements.
1952   const TNode<Smi> number_of_elements = SmiSub(
1953       CAST(LoadObjectField(table, OrderedHashSet::NumberOfElementsOffset())),
1954       SmiConstant(1));
1955   StoreObjectFieldNoWriteBarrier(
1956       table, OrderedHashSet::NumberOfElementsOffset(), number_of_elements);
1957   const TNode<Smi> number_of_deleted =
1958       SmiAdd(CAST(LoadObjectField(
1959                  table, OrderedHashSet::NumberOfDeletedElementsOffset())),
1960              SmiConstant(1));
1961   StoreObjectFieldNoWriteBarrier(
1962       table, OrderedHashSet::NumberOfDeletedElementsOffset(),
1963       number_of_deleted);
1964 
1965   const TNode<Smi> number_of_buckets = CAST(
1966       LoadFixedArrayElement(table, OrderedHashSet::NumberOfBucketsIndex()));
1967 
1968   // If there fewer elements than #buckets / 2, shrink the table.
1969   Label shrink(this);
1970   GotoIf(SmiLessThan(SmiAdd(number_of_elements, number_of_elements),
1971                      number_of_buckets),
1972          &shrink);
1973   Return(TrueConstant());
1974 
1975   BIND(&shrink);
1976   CallRuntime(Runtime::kSetShrink, context, receiver);
1977   Return(TrueConstant());
1978 }
1979 
TF_BUILTIN(MapPrototypeEntries,CollectionsBuiltinsAssembler)1980 TF_BUILTIN(MapPrototypeEntries, CollectionsBuiltinsAssembler) {
1981   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1982   const auto context = Parameter<Context>(Descriptor::kContext);
1983   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1984                          "Map.prototype.entries");
1985   Return(AllocateJSCollectionIterator<JSMapIterator>(
1986       context, Context::MAP_KEY_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
1987 }
1988 
TF_BUILTIN(MapPrototypeGetSize,CollectionsBuiltinsAssembler)1989 TF_BUILTIN(MapPrototypeGetSize, CollectionsBuiltinsAssembler) {
1990   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
1991   const auto context = Parameter<Context>(Descriptor::kContext);
1992   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
1993                          "get Map.prototype.size");
1994   const TNode<OrderedHashMap> table =
1995       LoadObjectField<OrderedHashMap>(CAST(receiver), JSMap::kTableOffset);
1996   Return(LoadObjectField(table, OrderedHashMap::NumberOfElementsOffset()));
1997 }
1998 
TF_BUILTIN(MapPrototypeForEach,CollectionsBuiltinsAssembler)1999 TF_BUILTIN(MapPrototypeForEach, CollectionsBuiltinsAssembler) {
2000   const char* const kMethodName = "Map.prototype.forEach";
2001   auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
2002   const auto context = Parameter<Context>(Descriptor::kContext);
2003   CodeStubArguments args(this, argc);
2004   const TNode<Object> receiver = args.GetReceiver();
2005   const TNode<Object> callback = args.GetOptionalArgumentValue(0);
2006   const TNode<Object> this_arg = args.GetOptionalArgumentValue(1);
2007 
2008   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, kMethodName);
2009 
2010   // Ensure that {callback} is actually callable.
2011   Label callback_not_callable(this, Label::kDeferred);
2012   GotoIf(TaggedIsSmi(callback), &callback_not_callable);
2013   GotoIfNot(IsCallable(CAST(callback)), &callback_not_callable);
2014 
2015   TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
2016   TVARIABLE(OrderedHashMap, var_table,
2017             CAST(LoadObjectField(CAST(receiver), JSMap::kTableOffset)));
2018   Label loop(this, {&var_index, &var_table}), done_loop(this);
2019   Goto(&loop);
2020   BIND(&loop);
2021   {
2022     // Transition {table} and {index} if there was any modification to
2023     // the {receiver} while we're iterating.
2024     TNode<IntPtrT> index = var_index.value();
2025     TNode<OrderedHashMap> table = var_table.value();
2026     std::tie(table, index) = Transition<OrderedHashMap>(
2027         table, index, [](const TNode<OrderedHashMap>, const TNode<IntPtrT>) {});
2028 
2029     // Read the next entry from the {table}, skipping holes.
2030     TNode<Object> entry_key;
2031     TNode<IntPtrT> entry_start_position;
2032     std::tie(entry_key, entry_start_position, index) =
2033         NextSkipHoles<OrderedHashMap>(table, index, &done_loop);
2034 
2035     // Load the entry value as well.
2036     TNode<Object> entry_value = LoadFixedArrayElement(
2037         table, entry_start_position,
2038         (OrderedHashMap::HashTableStartIndex() + OrderedHashMap::kValueOffset) *
2039             kTaggedSize);
2040 
2041     // Invoke the {callback} passing the {entry_key}, {entry_value} and the
2042     // {receiver}.
2043     Call(context, callback, this_arg, entry_value, entry_key, receiver);
2044 
2045     // Continue with the next entry.
2046     var_index = index;
2047     var_table = table;
2048     Goto(&loop);
2049   }
2050 
2051   BIND(&done_loop);
2052   args.PopAndReturn(UndefinedConstant());
2053 
2054   BIND(&callback_not_callable);
2055   {
2056     CallRuntime(Runtime::kThrowCalledNonCallable, context, callback);
2057     Unreachable();
2058   }
2059 }
2060 
TF_BUILTIN(MapPrototypeKeys,CollectionsBuiltinsAssembler)2061 TF_BUILTIN(MapPrototypeKeys, CollectionsBuiltinsAssembler) {
2062   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2063   const auto context = Parameter<Context>(Descriptor::kContext);
2064   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE, "Map.prototype.keys");
2065   Return(AllocateJSCollectionIterator<JSMapIterator>(
2066       context, Context::MAP_KEY_ITERATOR_MAP_INDEX, CAST(receiver)));
2067 }
2068 
TF_BUILTIN(MapPrototypeValues,CollectionsBuiltinsAssembler)2069 TF_BUILTIN(MapPrototypeValues, CollectionsBuiltinsAssembler) {
2070   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2071   const auto context = Parameter<Context>(Descriptor::kContext);
2072   ThrowIfNotInstanceType(context, receiver, JS_MAP_TYPE,
2073                          "Map.prototype.values");
2074   Return(AllocateJSCollectionIterator<JSMapIterator>(
2075       context, Context::MAP_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2076 }
2077 
TF_BUILTIN(MapIteratorPrototypeNext,CollectionsBuiltinsAssembler)2078 TF_BUILTIN(MapIteratorPrototypeNext, CollectionsBuiltinsAssembler) {
2079   const char* const kMethodName = "Map Iterator.prototype.next";
2080   const auto maybe_receiver = Parameter<Object>(Descriptor::kReceiver);
2081   const auto context = Parameter<Context>(Descriptor::kContext);
2082 
2083   // Ensure that {maybe_receiver} is actually a JSMapIterator.
2084   Label if_receiver_valid(this), if_receiver_invalid(this, Label::kDeferred);
2085   GotoIf(TaggedIsSmi(maybe_receiver), &if_receiver_invalid);
2086   const TNode<Uint16T> receiver_instance_type =
2087       LoadInstanceType(CAST(maybe_receiver));
2088   GotoIf(
2089       InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_VALUE_ITERATOR_TYPE),
2090       &if_receiver_valid);
2091   GotoIf(InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_ITERATOR_TYPE),
2092          &if_receiver_valid);
2093   Branch(InstanceTypeEqual(receiver_instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
2094          &if_receiver_valid, &if_receiver_invalid);
2095   BIND(&if_receiver_invalid);
2096   ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2097                  StringConstant(kMethodName), maybe_receiver);
2098   BIND(&if_receiver_valid);
2099   TNode<JSMapIterator> receiver = CAST(maybe_receiver);
2100 
2101   // Check if the {receiver} is exhausted.
2102   TVARIABLE(Oddball, var_done, TrueConstant());
2103   TVARIABLE(Object, var_value, UndefinedConstant());
2104   Label return_value(this, {&var_done, &var_value}), return_entry(this),
2105       return_end(this, Label::kDeferred);
2106 
2107   // Transition the {receiver} table if necessary.
2108   TNode<OrderedHashMap> table;
2109   TNode<IntPtrT> index;
2110   std::tie(table, index) =
2111       TransitionAndUpdate<JSMapIterator, OrderedHashMap>(receiver);
2112 
2113   // Read the next entry from the {table}, skipping holes.
2114   TNode<Object> entry_key;
2115   TNode<IntPtrT> entry_start_position;
2116   std::tie(entry_key, entry_start_position, index) =
2117       NextSkipHoles<OrderedHashMap>(table, index, &return_end);
2118   StoreObjectFieldNoWriteBarrier(receiver, JSMapIterator::kIndexOffset,
2119                                  SmiTag(index));
2120   var_value = entry_key;
2121   var_done = FalseConstant();
2122 
2123   // Check how to return the {key} (depending on {receiver} type).
2124   GotoIf(InstanceTypeEqual(receiver_instance_type, JS_MAP_KEY_ITERATOR_TYPE),
2125          &return_value);
2126   var_value = LoadFixedArrayElement(
2127       table, entry_start_position,
2128       (OrderedHashMap::HashTableStartIndex() + OrderedHashMap::kValueOffset) *
2129           kTaggedSize);
2130   Branch(InstanceTypeEqual(receiver_instance_type, JS_MAP_VALUE_ITERATOR_TYPE),
2131          &return_value, &return_entry);
2132 
2133   BIND(&return_entry);
2134   {
2135     TNode<JSObject> result =
2136         AllocateJSIteratorResultForEntry(context, entry_key, var_value.value());
2137     Return(result);
2138   }
2139 
2140   BIND(&return_value);
2141   {
2142     TNode<JSObject> result =
2143         AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2144     Return(result);
2145   }
2146 
2147   BIND(&return_end);
2148   {
2149     StoreObjectFieldRoot(receiver, JSMapIterator::kTableOffset,
2150                          RootIndex::kEmptyOrderedHashMap);
2151     Goto(&return_value);
2152   }
2153 }
2154 
TF_BUILTIN(SetPrototypeHas,CollectionsBuiltinsAssembler)2155 TF_BUILTIN(SetPrototypeHas, CollectionsBuiltinsAssembler) {
2156   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2157   const auto key = Parameter<Object>(Descriptor::kKey);
2158   const auto context = Parameter<Context>(Descriptor::kContext);
2159 
2160   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, "Set.prototype.has");
2161 
2162   const TNode<Object> table =
2163       LoadObjectField(CAST(receiver), JSMap::kTableOffset);
2164 
2165   TVARIABLE(IntPtrT, entry_start_position, IntPtrConstant(0));
2166   Label if_key_smi(this), if_key_string(this), if_key_heap_number(this),
2167       if_key_bigint(this), entry_found(this), not_found(this), done(this);
2168 
2169   GotoIf(TaggedIsSmi(key), &if_key_smi);
2170 
2171   TNode<Map> key_map = LoadMap(CAST(key));
2172   TNode<Uint16T> key_instance_type = LoadMapInstanceType(key_map);
2173 
2174   GotoIf(IsStringInstanceType(key_instance_type), &if_key_string);
2175   GotoIf(IsHeapNumberMap(key_map), &if_key_heap_number);
2176   GotoIf(IsBigIntInstanceType(key_instance_type), &if_key_bigint);
2177 
2178   FindOrderedHashTableEntryForOtherKey<OrderedHashSet>(
2179       CAST(table), CAST(key), &entry_start_position, &entry_found, &not_found);
2180 
2181   BIND(&if_key_smi);
2182   {
2183     FindOrderedHashTableEntryForSmiKey<OrderedHashSet>(
2184         CAST(table), CAST(key), &entry_start_position, &entry_found,
2185         &not_found);
2186   }
2187 
2188   BIND(&if_key_string);
2189   {
2190     FindOrderedHashTableEntryForStringKey<OrderedHashSet>(
2191         CAST(table), CAST(key), &entry_start_position, &entry_found,
2192         &not_found);
2193   }
2194 
2195   BIND(&if_key_heap_number);
2196   {
2197     FindOrderedHashTableEntryForHeapNumberKey<OrderedHashSet>(
2198         CAST(table), CAST(key), &entry_start_position, &entry_found,
2199         &not_found);
2200   }
2201 
2202   BIND(&if_key_bigint);
2203   {
2204     FindOrderedHashTableEntryForBigIntKey<OrderedHashSet>(
2205         CAST(table), CAST(key), &entry_start_position, &entry_found,
2206         &not_found);
2207   }
2208 
2209   BIND(&entry_found);
2210   Return(TrueConstant());
2211 
2212   BIND(&not_found);
2213   Return(FalseConstant());
2214 }
2215 
TF_BUILTIN(SetPrototypeEntries,CollectionsBuiltinsAssembler)2216 TF_BUILTIN(SetPrototypeEntries, CollectionsBuiltinsAssembler) {
2217   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2218   const auto context = Parameter<Context>(Descriptor::kContext);
2219   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2220                          "Set.prototype.entries");
2221   Return(AllocateJSCollectionIterator<JSSetIterator>(
2222       context, Context::SET_KEY_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2223 }
2224 
TF_BUILTIN(SetPrototypeGetSize,CollectionsBuiltinsAssembler)2225 TF_BUILTIN(SetPrototypeGetSize, CollectionsBuiltinsAssembler) {
2226   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2227   const auto context = Parameter<Context>(Descriptor::kContext);
2228   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2229                          "get Set.prototype.size");
2230   const TNode<OrderedHashSet> table =
2231       LoadObjectField<OrderedHashSet>(CAST(receiver), JSSet::kTableOffset);
2232   Return(LoadObjectField(table, OrderedHashSet::NumberOfElementsOffset()));
2233 }
2234 
TF_BUILTIN(SetPrototypeForEach,CollectionsBuiltinsAssembler)2235 TF_BUILTIN(SetPrototypeForEach, CollectionsBuiltinsAssembler) {
2236   const char* const kMethodName = "Set.prototype.forEach";
2237   auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount);
2238   const auto context = Parameter<Context>(Descriptor::kContext);
2239   CodeStubArguments args(this, argc);
2240   const TNode<Object> receiver = args.GetReceiver();
2241   const TNode<Object> callback = args.GetOptionalArgumentValue(0);
2242   const TNode<Object> this_arg = args.GetOptionalArgumentValue(1);
2243 
2244   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE, kMethodName);
2245 
2246   // Ensure that {callback} is actually callable.
2247   Label callback_not_callable(this, Label::kDeferred);
2248   GotoIf(TaggedIsSmi(callback), &callback_not_callable);
2249   GotoIfNot(IsCallable(CAST(callback)), &callback_not_callable);
2250 
2251   TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
2252   TVARIABLE(OrderedHashSet, var_table,
2253             CAST(LoadObjectField(CAST(receiver), JSSet::kTableOffset)));
2254   Label loop(this, {&var_index, &var_table}), done_loop(this);
2255   Goto(&loop);
2256   BIND(&loop);
2257   {
2258     // Transition {table} and {index} if there was any modification to
2259     // the {receiver} while we're iterating.
2260     TNode<IntPtrT> index = var_index.value();
2261     TNode<OrderedHashSet> table = var_table.value();
2262     std::tie(table, index) = Transition<OrderedHashSet>(
2263         table, index, [](const TNode<OrderedHashSet>, const TNode<IntPtrT>) {});
2264 
2265     // Read the next entry from the {table}, skipping holes.
2266     TNode<Object> entry_key;
2267     TNode<IntPtrT> entry_start_position;
2268     std::tie(entry_key, entry_start_position, index) =
2269         NextSkipHoles<OrderedHashSet>(table, index, &done_loop);
2270 
2271     // Invoke the {callback} passing the {entry_key} (twice) and the {receiver}.
2272     Call(context, callback, this_arg, entry_key, entry_key, receiver);
2273 
2274     // Continue with the next entry.
2275     var_index = index;
2276     var_table = table;
2277     Goto(&loop);
2278   }
2279 
2280   BIND(&done_loop);
2281   args.PopAndReturn(UndefinedConstant());
2282 
2283   BIND(&callback_not_callable);
2284   {
2285     CallRuntime(Runtime::kThrowCalledNonCallable, context, callback);
2286     Unreachable();
2287   }
2288 }
2289 
TF_BUILTIN(SetPrototypeValues,CollectionsBuiltinsAssembler)2290 TF_BUILTIN(SetPrototypeValues, CollectionsBuiltinsAssembler) {
2291   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2292   const auto context = Parameter<Context>(Descriptor::kContext);
2293   ThrowIfNotInstanceType(context, receiver, JS_SET_TYPE,
2294                          "Set.prototype.values");
2295   Return(AllocateJSCollectionIterator<JSSetIterator>(
2296       context, Context::SET_VALUE_ITERATOR_MAP_INDEX, CAST(receiver)));
2297 }
2298 
TF_BUILTIN(SetIteratorPrototypeNext,CollectionsBuiltinsAssembler)2299 TF_BUILTIN(SetIteratorPrototypeNext, CollectionsBuiltinsAssembler) {
2300   const char* const kMethodName = "Set Iterator.prototype.next";
2301   const auto maybe_receiver = Parameter<Object>(Descriptor::kReceiver);
2302   const auto context = Parameter<Context>(Descriptor::kContext);
2303 
2304   // Ensure that {maybe_receiver} is actually a JSSetIterator.
2305   Label if_receiver_valid(this), if_receiver_invalid(this, Label::kDeferred);
2306   GotoIf(TaggedIsSmi(maybe_receiver), &if_receiver_invalid);
2307   const TNode<Uint16T> receiver_instance_type =
2308       LoadInstanceType(CAST(maybe_receiver));
2309   GotoIf(InstanceTypeEqual(receiver_instance_type, JS_SET_VALUE_ITERATOR_TYPE),
2310          &if_receiver_valid);
2311   Branch(
2312       InstanceTypeEqual(receiver_instance_type, JS_SET_KEY_VALUE_ITERATOR_TYPE),
2313       &if_receiver_valid, &if_receiver_invalid);
2314   BIND(&if_receiver_invalid);
2315   ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
2316                  StringConstant(kMethodName), maybe_receiver);
2317   BIND(&if_receiver_valid);
2318 
2319   TNode<JSSetIterator> receiver = CAST(maybe_receiver);
2320 
2321   // Check if the {receiver} is exhausted.
2322   TVARIABLE(Oddball, var_done, TrueConstant());
2323   TVARIABLE(Object, var_value, UndefinedConstant());
2324   Label return_value(this, {&var_done, &var_value}), return_entry(this),
2325       return_end(this, Label::kDeferred);
2326 
2327   // Transition the {receiver} table if necessary.
2328   TNode<OrderedHashSet> table;
2329   TNode<IntPtrT> index;
2330   std::tie(table, index) =
2331       TransitionAndUpdate<JSSetIterator, OrderedHashSet>(receiver);
2332 
2333   // Read the next entry from the {table}, skipping holes.
2334   TNode<Object> entry_key;
2335   TNode<IntPtrT> entry_start_position;
2336   std::tie(entry_key, entry_start_position, index) =
2337       NextSkipHoles<OrderedHashSet>(table, index, &return_end);
2338   StoreObjectFieldNoWriteBarrier(receiver, JSSetIterator::kIndexOffset,
2339                                  SmiTag(index));
2340   var_value = entry_key;
2341   var_done = FalseConstant();
2342 
2343   // Check how to return the {key} (depending on {receiver} type).
2344   Branch(InstanceTypeEqual(receiver_instance_type, JS_SET_VALUE_ITERATOR_TYPE),
2345          &return_value, &return_entry);
2346 
2347   BIND(&return_entry);
2348   {
2349     TNode<JSObject> result = AllocateJSIteratorResultForEntry(
2350         context, var_value.value(), var_value.value());
2351     Return(result);
2352   }
2353 
2354   BIND(&return_value);
2355   {
2356     TNode<JSObject> result =
2357         AllocateJSIteratorResult(context, var_value.value(), var_done.value());
2358     Return(result);
2359   }
2360 
2361   BIND(&return_end);
2362   {
2363     StoreObjectFieldRoot(receiver, JSSetIterator::kTableOffset,
2364                          RootIndex::kEmptyOrderedHashSet);
2365     Goto(&return_value);
2366   }
2367 }
2368 
2369 template <typename CollectionType>
TryLookupOrderedHashTableIndex(const TNode<CollectionType> table,const TNode<Object> key,TVariable<IntPtrT> * result,Label * if_entry_found,Label * if_not_found)2370 void CollectionsBuiltinsAssembler::TryLookupOrderedHashTableIndex(
2371     const TNode<CollectionType> table, const TNode<Object> key,
2372     TVariable<IntPtrT>* result, Label* if_entry_found, Label* if_not_found) {
2373   Label if_key_smi(this), if_key_string(this), if_key_heap_number(this),
2374       if_key_bigint(this);
2375 
2376   GotoIf(TaggedIsSmi(key), &if_key_smi);
2377 
2378   TNode<Map> key_map = LoadMap(CAST(key));
2379   TNode<Uint16T> key_instance_type = LoadMapInstanceType(key_map);
2380 
2381   GotoIf(IsStringInstanceType(key_instance_type), &if_key_string);
2382   GotoIf(IsHeapNumberMap(key_map), &if_key_heap_number);
2383   GotoIf(IsBigIntInstanceType(key_instance_type), &if_key_bigint);
2384 
2385   FindOrderedHashTableEntryForOtherKey<CollectionType>(
2386       table, CAST(key), result, if_entry_found, if_not_found);
2387 
2388   BIND(&if_key_smi);
2389   {
2390     FindOrderedHashTableEntryForSmiKey<CollectionType>(
2391         table, CAST(key), result, if_entry_found, if_not_found);
2392   }
2393 
2394   BIND(&if_key_string);
2395   {
2396     FindOrderedHashTableEntryForStringKey<CollectionType>(
2397         table, CAST(key), result, if_entry_found, if_not_found);
2398   }
2399 
2400   BIND(&if_key_heap_number);
2401   {
2402     FindOrderedHashTableEntryForHeapNumberKey<CollectionType>(
2403         table, CAST(key), result, if_entry_found, if_not_found);
2404   }
2405 
2406   BIND(&if_key_bigint);
2407   {
2408     FindOrderedHashTableEntryForBigIntKey<CollectionType>(
2409         table, CAST(key), result, if_entry_found, if_not_found);
2410   }
2411 }
2412 
TF_BUILTIN(FindOrderedHashMapEntry,CollectionsBuiltinsAssembler)2413 TF_BUILTIN(FindOrderedHashMapEntry, CollectionsBuiltinsAssembler) {
2414   const auto table = Parameter<OrderedHashMap>(Descriptor::kTable);
2415   const auto key = Parameter<Object>(Descriptor::kKey);
2416 
2417   TVARIABLE(IntPtrT, entry_start_position, IntPtrConstant(0));
2418   Label entry_found(this), not_found(this);
2419 
2420   TryLookupOrderedHashTableIndex<OrderedHashMap>(
2421       table, key, &entry_start_position, &entry_found, &not_found);
2422 
2423   BIND(&entry_found);
2424   Return(SmiTag(entry_start_position.value()));
2425 
2426   BIND(&not_found);
2427   Return(SmiConstant(-1));
2428 }
2429 
2430 class WeakCollectionsBuiltinsAssembler : public BaseCollectionsAssembler {
2431  public:
WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState * state)2432   explicit WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state)
2433       : BaseCollectionsAssembler(state) {}
2434 
2435  protected:
2436   void AddEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
2437                 TNode<Object> key, TNode<Object> value,
2438                 TNode<IntPtrT> number_of_elements);
2439 
2440   TNode<HeapObject> AllocateTable(Variant variant,
2441                                   TNode<IntPtrT> at_least_space_for) override;
2442 
2443   // Generates and sets the identity for a JSRececiver.
2444   TNode<Smi> CreateIdentityHash(TNode<Object> receiver);
2445   TNode<IntPtrT> EntryMask(TNode<IntPtrT> capacity);
2446 
2447   // Builds code that finds the EphemeronHashTable entry for a {key} using the
2448   // comparison code generated by {key_compare}. The key index is returned if
2449   // the {key} is found.
2450   using KeyComparator =
2451       std::function<void(TNode<Object> entry_key, Label* if_same)>;
2452   TNode<IntPtrT> FindKeyIndex(TNode<HeapObject> table, TNode<IntPtrT> key_hash,
2453                               TNode<IntPtrT> entry_mask,
2454                               const KeyComparator& key_compare);
2455 
2456   // Builds code that finds an EphemeronHashTable entry available for a new
2457   // entry.
2458   TNode<IntPtrT> FindKeyIndexForInsertion(TNode<HeapObject> table,
2459                                           TNode<IntPtrT> key_hash,
2460                                           TNode<IntPtrT> entry_mask);
2461 
2462   // Builds code that finds the EphemeronHashTable entry with key that matches
2463   // {key} and returns the entry's key index. If {key} cannot be found, jumps to
2464   // {if_not_found}.
2465   TNode<IntPtrT> FindKeyIndexForKey(TNode<HeapObject> table, TNode<Object> key,
2466                                     TNode<IntPtrT> hash,
2467                                     TNode<IntPtrT> entry_mask,
2468                                     Label* if_not_found);
2469 
2470   TNode<Word32T> InsufficientCapacityToAdd(TNode<IntPtrT> capacity,
2471                                            TNode<IntPtrT> number_of_elements,
2472                                            TNode<IntPtrT> number_of_deleted);
2473   TNode<IntPtrT> KeyIndexFromEntry(TNode<IntPtrT> entry);
2474 
2475   TNode<IntPtrT> LoadNumberOfElements(TNode<EphemeronHashTable> table,
2476                                       int offset);
2477   TNode<IntPtrT> LoadNumberOfDeleted(TNode<EphemeronHashTable> table,
2478                                      int offset = 0);
2479   TNode<EphemeronHashTable> LoadTable(TNode<JSWeakCollection> collection);
2480   TNode<IntPtrT> LoadTableCapacity(TNode<EphemeronHashTable> table);
2481 
2482   void RemoveEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
2483                    TNode<IntPtrT> number_of_elements);
2484   TNode<BoolT> ShouldRehash(TNode<IntPtrT> number_of_elements,
2485                             TNode<IntPtrT> number_of_deleted);
2486   TNode<Word32T> ShouldShrink(TNode<IntPtrT> capacity,
2487                               TNode<IntPtrT> number_of_elements);
2488   TNode<IntPtrT> ValueIndexFromKeyIndex(TNode<IntPtrT> key_index);
2489 };
2490 
AddEntry(TNode<EphemeronHashTable> table,TNode<IntPtrT> key_index,TNode<Object> key,TNode<Object> value,TNode<IntPtrT> number_of_elements)2491 void WeakCollectionsBuiltinsAssembler::AddEntry(
2492     TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
2493     TNode<Object> key, TNode<Object> value, TNode<IntPtrT> number_of_elements) {
2494   // See EphemeronHashTable::AddEntry().
2495   TNode<IntPtrT> value_index = ValueIndexFromKeyIndex(key_index);
2496   UnsafeStoreFixedArrayElement(table, key_index, key,
2497                                UPDATE_EPHEMERON_KEY_WRITE_BARRIER);
2498   UnsafeStoreFixedArrayElement(table, value_index, value);
2499 
2500   // See HashTableBase::ElementAdded().
2501   UnsafeStoreFixedArrayElement(table,
2502                                EphemeronHashTable::kNumberOfElementsIndex,
2503                                SmiFromIntPtr(number_of_elements));
2504 }
2505 
AllocateTable(Variant variant,TNode<IntPtrT> at_least_space_for)2506 TNode<HeapObject> WeakCollectionsBuiltinsAssembler::AllocateTable(
2507     Variant variant, TNode<IntPtrT> at_least_space_for) {
2508   // See HashTable::New().
2509   CSA_DCHECK(this,
2510              IntPtrLessThanOrEqual(IntPtrConstant(0), at_least_space_for));
2511   TNode<IntPtrT> capacity = HashTableComputeCapacity(at_least_space_for);
2512 
2513   // See HashTable::NewInternal().
2514   TNode<IntPtrT> length = KeyIndexFromEntry(capacity);
2515   TNode<FixedArray> table = CAST(AllocateFixedArray(
2516       HOLEY_ELEMENTS, length, AllocationFlag::kAllowLargeObjectAllocation));
2517 
2518   TNode<Map> map =
2519       HeapConstant(EphemeronHashTable::GetMap(ReadOnlyRoots(isolate())));
2520   StoreMapNoWriteBarrier(table, map);
2521   StoreFixedArrayElement(table, EphemeronHashTable::kNumberOfElementsIndex,
2522                          SmiConstant(0), SKIP_WRITE_BARRIER);
2523   StoreFixedArrayElement(table,
2524                          EphemeronHashTable::kNumberOfDeletedElementsIndex,
2525                          SmiConstant(0), SKIP_WRITE_BARRIER);
2526   StoreFixedArrayElement(table, EphemeronHashTable::kCapacityIndex,
2527                          SmiFromIntPtr(capacity), SKIP_WRITE_BARRIER);
2528 
2529   TNode<IntPtrT> start = KeyIndexFromEntry(IntPtrConstant(0));
2530   FillFixedArrayWithValue(HOLEY_ELEMENTS, table, start, length,
2531                           RootIndex::kUndefinedValue);
2532   return table;
2533 }
2534 
CreateIdentityHash(TNode<Object> key)2535 TNode<Smi> WeakCollectionsBuiltinsAssembler::CreateIdentityHash(
2536     TNode<Object> key) {
2537   TNode<ExternalReference> function_addr =
2538       ExternalConstant(ExternalReference::jsreceiver_create_identity_hash());
2539   TNode<ExternalReference> isolate_ptr =
2540       ExternalConstant(ExternalReference::isolate_address(isolate()));
2541 
2542   MachineType type_ptr = MachineType::Pointer();
2543   MachineType type_tagged = MachineType::AnyTagged();
2544 
2545   return CAST(CallCFunction(function_addr, type_tagged,
2546                             std::make_pair(type_ptr, isolate_ptr),
2547                             std::make_pair(type_tagged, key)));
2548 }
2549 
EntryMask(TNode<IntPtrT> capacity)2550 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::EntryMask(
2551     TNode<IntPtrT> capacity) {
2552   return IntPtrSub(capacity, IntPtrConstant(1));
2553 }
2554 
FindKeyIndex(TNode<HeapObject> table,TNode<IntPtrT> key_hash,TNode<IntPtrT> entry_mask,const KeyComparator & key_compare)2555 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::FindKeyIndex(
2556     TNode<HeapObject> table, TNode<IntPtrT> key_hash, TNode<IntPtrT> entry_mask,
2557     const KeyComparator& key_compare) {
2558   // See HashTable::FirstProbe().
2559   TVARIABLE(IntPtrT, var_entry, WordAnd(key_hash, entry_mask));
2560   TVARIABLE(IntPtrT, var_count, IntPtrConstant(0));
2561 
2562   Label loop(this, {&var_count, &var_entry}), if_found(this);
2563   Goto(&loop);
2564   BIND(&loop);
2565   TNode<IntPtrT> key_index;
2566   {
2567     key_index = KeyIndexFromEntry(var_entry.value());
2568     TNode<Object> entry_key =
2569         UnsafeLoadFixedArrayElement(CAST(table), key_index);
2570 
2571     key_compare(entry_key, &if_found);
2572 
2573     // See HashTable::NextProbe().
2574     Increment(&var_count);
2575     var_entry =
2576         WordAnd(IntPtrAdd(var_entry.value(), var_count.value()), entry_mask);
2577     Goto(&loop);
2578   }
2579 
2580   BIND(&if_found);
2581   return key_index;
2582 }
2583 
FindKeyIndexForInsertion(TNode<HeapObject> table,TNode<IntPtrT> key_hash,TNode<IntPtrT> entry_mask)2584 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::FindKeyIndexForInsertion(
2585     TNode<HeapObject> table, TNode<IntPtrT> key_hash,
2586     TNode<IntPtrT> entry_mask) {
2587   // See HashTable::FindInsertionEntry().
2588   auto is_not_live = [&](TNode<Object> entry_key, Label* if_found) {
2589     // This is the the negative form BaseShape::IsLive().
2590     GotoIf(Word32Or(IsTheHole(entry_key), IsUndefined(entry_key)), if_found);
2591   };
2592   return FindKeyIndex(table, key_hash, entry_mask, is_not_live);
2593 }
2594 
FindKeyIndexForKey(TNode<HeapObject> table,TNode<Object> key,TNode<IntPtrT> hash,TNode<IntPtrT> entry_mask,Label * if_not_found)2595 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::FindKeyIndexForKey(
2596     TNode<HeapObject> table, TNode<Object> key, TNode<IntPtrT> hash,
2597     TNode<IntPtrT> entry_mask, Label* if_not_found) {
2598   // See HashTable::FindEntry().
2599   auto match_key_or_exit_on_empty = [&](TNode<Object> entry_key,
2600                                         Label* if_same) {
2601     GotoIf(IsUndefined(entry_key), if_not_found);
2602     GotoIf(TaggedEqual(entry_key, key), if_same);
2603   };
2604   return FindKeyIndex(table, hash, entry_mask, match_key_or_exit_on_empty);
2605 }
2606 
KeyIndexFromEntry(TNode<IntPtrT> entry)2607 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::KeyIndexFromEntry(
2608     TNode<IntPtrT> entry) {
2609   // See HashTable::KeyAt().
2610   // (entry * kEntrySize) + kElementsStartIndex + kEntryKeyIndex
2611   return IntPtrAdd(
2612       IntPtrMul(entry, IntPtrConstant(EphemeronHashTable::kEntrySize)),
2613       IntPtrConstant(EphemeronHashTable::kElementsStartIndex +
2614                      EphemeronHashTable::kEntryKeyIndex));
2615 }
2616 
LoadNumberOfElements(TNode<EphemeronHashTable> table,int offset)2617 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::LoadNumberOfElements(
2618     TNode<EphemeronHashTable> table, int offset) {
2619   TNode<IntPtrT> number_of_elements = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
2620       table, EphemeronHashTable::kNumberOfElementsIndex)));
2621   return IntPtrAdd(number_of_elements, IntPtrConstant(offset));
2622 }
2623 
LoadNumberOfDeleted(TNode<EphemeronHashTable> table,int offset)2624 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::LoadNumberOfDeleted(
2625     TNode<EphemeronHashTable> table, int offset) {
2626   TNode<IntPtrT> number_of_deleted = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
2627       table, EphemeronHashTable::kNumberOfDeletedElementsIndex)));
2628   return IntPtrAdd(number_of_deleted, IntPtrConstant(offset));
2629 }
2630 
LoadTable(TNode<JSWeakCollection> collection)2631 TNode<EphemeronHashTable> WeakCollectionsBuiltinsAssembler::LoadTable(
2632     TNode<JSWeakCollection> collection) {
2633   return CAST(LoadObjectField(collection, JSWeakCollection::kTableOffset));
2634 }
2635 
LoadTableCapacity(TNode<EphemeronHashTable> table)2636 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::LoadTableCapacity(
2637     TNode<EphemeronHashTable> table) {
2638   return SmiUntag(CAST(
2639       UnsafeLoadFixedArrayElement(table, EphemeronHashTable::kCapacityIndex)));
2640 }
2641 
InsufficientCapacityToAdd(TNode<IntPtrT> capacity,TNode<IntPtrT> number_of_elements,TNode<IntPtrT> number_of_deleted)2642 TNode<Word32T> WeakCollectionsBuiltinsAssembler::InsufficientCapacityToAdd(
2643     TNode<IntPtrT> capacity, TNode<IntPtrT> number_of_elements,
2644     TNode<IntPtrT> number_of_deleted) {
2645   // This is the negative form of HashTable::HasSufficientCapacityToAdd().
2646   // Return true if:
2647   //   - more than 50% of the available space are deleted elements
2648   //   - less than 50% will be available
2649   TNode<IntPtrT> available = IntPtrSub(capacity, number_of_elements);
2650   TNode<IntPtrT> half_available = WordShr(available, 1);
2651   TNode<IntPtrT> needed_available = WordShr(number_of_elements, 1);
2652   return Word32Or(
2653       // deleted > half
2654       IntPtrGreaterThan(number_of_deleted, half_available),
2655       // elements + needed available > capacity
2656       IntPtrGreaterThan(IntPtrAdd(number_of_elements, needed_available),
2657                         capacity));
2658 }
2659 
RemoveEntry(TNode<EphemeronHashTable> table,TNode<IntPtrT> key_index,TNode<IntPtrT> number_of_elements)2660 void WeakCollectionsBuiltinsAssembler::RemoveEntry(
2661     TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
2662     TNode<IntPtrT> number_of_elements) {
2663   // See EphemeronHashTable::RemoveEntry().
2664   TNode<IntPtrT> value_index = ValueIndexFromKeyIndex(key_index);
2665   StoreFixedArrayElement(table, key_index, TheHoleConstant());
2666   StoreFixedArrayElement(table, value_index, TheHoleConstant());
2667 
2668   // See HashTableBase::ElementRemoved().
2669   TNode<IntPtrT> number_of_deleted = LoadNumberOfDeleted(table, 1);
2670   StoreFixedArrayElement(table, EphemeronHashTable::kNumberOfElementsIndex,
2671                          SmiFromIntPtr(number_of_elements), SKIP_WRITE_BARRIER);
2672   StoreFixedArrayElement(table,
2673                          EphemeronHashTable::kNumberOfDeletedElementsIndex,
2674                          SmiFromIntPtr(number_of_deleted), SKIP_WRITE_BARRIER);
2675 }
2676 
ShouldRehash(TNode<IntPtrT> number_of_elements,TNode<IntPtrT> number_of_deleted)2677 TNode<BoolT> WeakCollectionsBuiltinsAssembler::ShouldRehash(
2678     TNode<IntPtrT> number_of_elements, TNode<IntPtrT> number_of_deleted) {
2679   // Rehash if more than 33% of the entries are deleted.
2680   return IntPtrGreaterThanOrEqual(WordShl(number_of_deleted, 1),
2681                                   number_of_elements);
2682 }
2683 
ShouldShrink(TNode<IntPtrT> capacity,TNode<IntPtrT> number_of_elements)2684 TNode<Word32T> WeakCollectionsBuiltinsAssembler::ShouldShrink(
2685     TNode<IntPtrT> capacity, TNode<IntPtrT> number_of_elements) {
2686   // See HashTable::Shrink().
2687   TNode<IntPtrT> quarter_capacity = WordShr(capacity, 2);
2688   return Word32And(
2689       // Shrink to fit the number of elements if only a quarter of the
2690       // capacity is filled with elements.
2691       IntPtrLessThanOrEqual(number_of_elements, quarter_capacity),
2692 
2693       // Allocate a new dictionary with room for at least the current
2694       // number of elements. The allocation method will make sure that
2695       // there is extra room in the dictionary for additions. Don't go
2696       // lower than room for 16 elements.
2697       IntPtrGreaterThanOrEqual(number_of_elements, IntPtrConstant(16)));
2698 }
2699 
ValueIndexFromKeyIndex(TNode<IntPtrT> key_index)2700 TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::ValueIndexFromKeyIndex(
2701     TNode<IntPtrT> key_index) {
2702   return IntPtrAdd(key_index,
2703                    IntPtrConstant(EphemeronHashTable::ShapeT::kEntryValueIndex -
2704                                   EphemeronHashTable::kEntryKeyIndex));
2705 }
2706 
TF_BUILTIN(WeakMapConstructor,WeakCollectionsBuiltinsAssembler)2707 TF_BUILTIN(WeakMapConstructor, WeakCollectionsBuiltinsAssembler) {
2708   auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
2709   TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
2710       UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
2711   auto context = Parameter<Context>(Descriptor::kContext);
2712 
2713   GenerateConstructor(kWeakMap, isolate()->factory()->WeakMap_string(),
2714                       new_target, argc, context);
2715 }
2716 
TF_BUILTIN(WeakSetConstructor,WeakCollectionsBuiltinsAssembler)2717 TF_BUILTIN(WeakSetConstructor, WeakCollectionsBuiltinsAssembler) {
2718   auto new_target = Parameter<Object>(Descriptor::kJSNewTarget);
2719   TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
2720       UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
2721   auto context = Parameter<Context>(Descriptor::kContext);
2722 
2723   GenerateConstructor(kWeakSet, isolate()->factory()->WeakSet_string(),
2724                       new_target, argc, context);
2725 }
2726 
TF_BUILTIN(WeakMapLookupHashIndex,WeakCollectionsBuiltinsAssembler)2727 TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {
2728   auto table = Parameter<EphemeronHashTable>(Descriptor::kTable);
2729   auto key = Parameter<Object>(Descriptor::kKey);
2730 
2731   Label if_cannot_be_weak_key(this);
2732 
2733   GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
2734 
2735   TNode<IntPtrT> hash =
2736       LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
2737   TNode<IntPtrT> capacity = LoadTableCapacity(table);
2738   TNode<IntPtrT> key_index = FindKeyIndexForKey(
2739       table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
2740   Return(SmiTag(ValueIndexFromKeyIndex(key_index)));
2741 
2742   BIND(&if_cannot_be_weak_key);
2743   Return(SmiConstant(-1));
2744 }
2745 
TF_BUILTIN(WeakMapGet,WeakCollectionsBuiltinsAssembler)2746 TF_BUILTIN(WeakMapGet, WeakCollectionsBuiltinsAssembler) {
2747   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2748   const auto key = Parameter<Object>(Descriptor::kKey);
2749   const auto context = Parameter<Context>(Descriptor::kContext);
2750 
2751   Label return_undefined(this);
2752 
2753   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2754                          "WeakMap.prototype.get");
2755 
2756   const TNode<EphemeronHashTable> table = LoadTable(CAST(receiver));
2757   const TNode<Smi> index =
2758       CAST(CallBuiltin(Builtin::kWeakMapLookupHashIndex, context, table, key));
2759 
2760   GotoIf(TaggedEqual(index, SmiConstant(-1)), &return_undefined);
2761 
2762   Return(LoadFixedArrayElement(table, SmiUntag(index)));
2763 
2764   BIND(&return_undefined);
2765   Return(UndefinedConstant());
2766 }
2767 
TF_BUILTIN(WeakMapPrototypeHas,WeakCollectionsBuiltinsAssembler)2768 TF_BUILTIN(WeakMapPrototypeHas, WeakCollectionsBuiltinsAssembler) {
2769   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2770   const auto key = Parameter<Object>(Descriptor::kKey);
2771   const auto context = Parameter<Context>(Descriptor::kContext);
2772 
2773   Label return_false(this);
2774 
2775   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2776                          "WeakMap.prototype.has");
2777 
2778   const TNode<EphemeronHashTable> table = LoadTable(CAST(receiver));
2779   const TNode<Object> index =
2780       CallBuiltin(Builtin::kWeakMapLookupHashIndex, context, table, key);
2781 
2782   GotoIf(TaggedEqual(index, SmiConstant(-1)), &return_false);
2783 
2784   Return(TrueConstant());
2785 
2786   BIND(&return_false);
2787   Return(FalseConstant());
2788 }
2789 
2790 // Helper that removes the entry with a given key from the backing store
2791 // (EphemeronHashTable) of a WeakMap or WeakSet.
TF_BUILTIN(WeakCollectionDelete,WeakCollectionsBuiltinsAssembler)2792 TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
2793   auto context = Parameter<Context>(Descriptor::kContext);
2794   auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
2795   auto key = Parameter<Object>(Descriptor::kKey);
2796 
2797   Label call_runtime(this), if_cannot_be_weak_key(this);
2798 
2799   GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
2800 
2801   TNode<IntPtrT> hash =
2802       LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
2803   TNode<EphemeronHashTable> table = LoadTable(collection);
2804   TNode<IntPtrT> capacity = LoadTableCapacity(table);
2805   TNode<IntPtrT> key_index = FindKeyIndexForKey(
2806       table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
2807   TNode<IntPtrT> number_of_elements = LoadNumberOfElements(table, -1);
2808   GotoIf(ShouldShrink(capacity, number_of_elements), &call_runtime);
2809 
2810   RemoveEntry(table, key_index, number_of_elements);
2811   Return(TrueConstant());
2812 
2813   BIND(&if_cannot_be_weak_key);
2814   Return(FalseConstant());
2815 
2816   BIND(&call_runtime);
2817   Return(CallRuntime(Runtime::kWeakCollectionDelete, context, collection, key,
2818                      SmiTag(hash)));
2819 }
2820 
2821 // Helper that sets the key and value to the backing store (EphemeronHashTable)
2822 // of a WeakMap or WeakSet.
TF_BUILTIN(WeakCollectionSet,WeakCollectionsBuiltinsAssembler)2823 TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
2824   auto context = Parameter<Context>(Descriptor::kContext);
2825   auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
2826   auto key = Parameter<JSReceiver>(Descriptor::kKey);
2827   auto value = Parameter<Object>(Descriptor::kValue);
2828 
2829   CSA_DCHECK(this, IsJSReceiver(key));
2830 
2831   Label call_runtime(this), if_no_hash(this), if_not_found(this);
2832 
2833   TNode<EphemeronHashTable> table = LoadTable(collection);
2834   TNode<IntPtrT> capacity = LoadTableCapacity(table);
2835   TNode<IntPtrT> entry_mask = EntryMask(capacity);
2836 
2837   TVARIABLE(IntPtrT, var_hash, LoadJSReceiverIdentityHash(key, &if_no_hash));
2838   TNode<IntPtrT> key_index = FindKeyIndexForKey(table, key, var_hash.value(),
2839                                                 entry_mask, &if_not_found);
2840 
2841   StoreFixedArrayElement(table, ValueIndexFromKeyIndex(key_index), value);
2842   Return(collection);
2843 
2844   BIND(&if_no_hash);
2845   {
2846     var_hash = SmiUntag(CreateIdentityHash(key));
2847     Goto(&if_not_found);
2848   }
2849   BIND(&if_not_found);
2850   {
2851     TNode<IntPtrT> number_of_deleted = LoadNumberOfDeleted(table);
2852     TNode<IntPtrT> number_of_elements = LoadNumberOfElements(table, 1);
2853 
2854     // TODO(pwong): Port HashTable's Rehash() and EnsureCapacity() to CSA.
2855     GotoIf(Word32Or(ShouldRehash(number_of_elements, number_of_deleted),
2856                     InsufficientCapacityToAdd(capacity, number_of_elements,
2857                                               number_of_deleted)),
2858            &call_runtime);
2859 
2860     TNode<IntPtrT> insertion_key_index =
2861         FindKeyIndexForInsertion(table, var_hash.value(), entry_mask);
2862     AddEntry(table, insertion_key_index, key, value, number_of_elements);
2863     Return(collection);
2864   }
2865   BIND(&call_runtime);
2866   {
2867     CallRuntime(Runtime::kWeakCollectionSet, context, collection, key, value,
2868                 SmiTag(var_hash.value()));
2869     Return(collection);
2870   }
2871 }
2872 
TF_BUILTIN(WeakMapPrototypeDelete,CodeStubAssembler)2873 TF_BUILTIN(WeakMapPrototypeDelete, CodeStubAssembler) {
2874   auto context = Parameter<Context>(Descriptor::kContext);
2875   auto receiver = Parameter<Object>(Descriptor::kReceiver);
2876   auto key = Parameter<Object>(Descriptor::kKey);
2877 
2878   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2879                          "WeakMap.prototype.delete");
2880 
2881   Return(CallBuiltin(Builtin::kWeakCollectionDelete, context, receiver, key));
2882 }
2883 
TF_BUILTIN(WeakMapPrototypeSet,WeakCollectionsBuiltinsAssembler)2884 TF_BUILTIN(WeakMapPrototypeSet, WeakCollectionsBuiltinsAssembler) {
2885   auto context = Parameter<Context>(Descriptor::kContext);
2886   auto receiver = Parameter<Object>(Descriptor::kReceiver);
2887   auto key = Parameter<Object>(Descriptor::kKey);
2888   auto value = Parameter<Object>(Descriptor::kValue);
2889 
2890   ThrowIfNotInstanceType(context, receiver, JS_WEAK_MAP_TYPE,
2891                          "WeakMap.prototype.set");
2892 
2893   Label throw_invalid_key(this);
2894   GotoIfCannotBeWeakKey(key, &throw_invalid_key);
2895 
2896   Return(
2897       CallBuiltin(Builtin::kWeakCollectionSet, context, receiver, key, value));
2898 
2899   BIND(&throw_invalid_key);
2900   ThrowTypeError(context, MessageTemplate::kInvalidWeakMapKey, key);
2901 }
2902 
TF_BUILTIN(WeakSetPrototypeAdd,WeakCollectionsBuiltinsAssembler)2903 TF_BUILTIN(WeakSetPrototypeAdd, WeakCollectionsBuiltinsAssembler) {
2904   auto context = Parameter<Context>(Descriptor::kContext);
2905   auto receiver = Parameter<Object>(Descriptor::kReceiver);
2906   auto value = Parameter<Object>(Descriptor::kValue);
2907 
2908   ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
2909                          "WeakSet.prototype.add");
2910 
2911   Label throw_invalid_value(this);
2912   GotoIfCannotBeWeakKey(value, &throw_invalid_value);
2913 
2914   Return(CallBuiltin(Builtin::kWeakCollectionSet, context, receiver, value,
2915                      TrueConstant()));
2916 
2917   BIND(&throw_invalid_value);
2918   ThrowTypeError(context, MessageTemplate::kInvalidWeakSetValue, value);
2919 }
2920 
TF_BUILTIN(WeakSetPrototypeDelete,CodeStubAssembler)2921 TF_BUILTIN(WeakSetPrototypeDelete, CodeStubAssembler) {
2922   auto context = Parameter<Context>(Descriptor::kContext);
2923   auto receiver = Parameter<Object>(Descriptor::kReceiver);
2924   auto value = Parameter<Object>(Descriptor::kValue);
2925 
2926   ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
2927                          "WeakSet.prototype.delete");
2928 
2929   Return(CallBuiltin(Builtin::kWeakCollectionDelete, context, receiver, value));
2930 }
2931 
TF_BUILTIN(WeakSetPrototypeHas,WeakCollectionsBuiltinsAssembler)2932 TF_BUILTIN(WeakSetPrototypeHas, WeakCollectionsBuiltinsAssembler) {
2933   const auto receiver = Parameter<Object>(Descriptor::kReceiver);
2934   const auto key = Parameter<Object>(Descriptor::kKey);
2935   const auto context = Parameter<Context>(Descriptor::kContext);
2936 
2937   Label return_false(this);
2938 
2939   ThrowIfNotInstanceType(context, receiver, JS_WEAK_SET_TYPE,
2940                          "WeakSet.prototype.has");
2941 
2942   const TNode<EphemeronHashTable> table = LoadTable(CAST(receiver));
2943   const TNode<Object> index =
2944       CallBuiltin(Builtin::kWeakMapLookupHashIndex, context, table, key);
2945 
2946   GotoIf(TaggedEqual(index, SmiConstant(-1)), &return_false);
2947 
2948   Return(TrueConstant());
2949 
2950   BIND(&return_false);
2951   Return(FalseConstant());
2952 }
2953 
2954 }  // namespace internal
2955 }  // namespace v8
2956