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