• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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-constructor.h"
6 #include "src/ast/ast.h"
7 #include "src/builtins/builtins-utils.h"
8 #include "src/builtins/builtins.h"
9 #include "src/code-factory.h"
10 #include "src/code-stub-assembler.h"
11 #include "src/counters.h"
12 #include "src/interface-descriptors.h"
13 #include "src/objects-inl.h"
14 
15 namespace v8 {
16 namespace internal {
17 
18 typedef compiler::Node Node;
19 
EmitFastNewClosure(Node * shared_info,Node * feedback_vector,Node * slot,Node * context)20 Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
21                                                        Node* feedback_vector,
22                                                        Node* slot,
23                                                        Node* context) {
24   typedef compiler::CodeAssembler::Label Label;
25   typedef compiler::CodeAssembler::Variable Variable;
26 
27   Isolate* isolate = this->isolate();
28   Factory* factory = isolate->factory();
29   IncrementCounter(isolate->counters()->fast_new_closure_total(), 1);
30 
31   // Create a new closure from the given function info in new space
32   Node* result = Allocate(JSFunction::kSize);
33 
34   // Calculate the index of the map we should install on the function based on
35   // the FunctionKind and LanguageMode of the function.
36   // Note: Must be kept in sync with Context::FunctionMapIndex
37   Node* compiler_hints =
38       LoadObjectField(shared_info, SharedFunctionInfo::kCompilerHintsOffset,
39                       MachineType::Uint32());
40   Node* is_strict = Word32And(
41       compiler_hints, Int32Constant(1 << SharedFunctionInfo::kStrictModeBit));
42 
43   Label if_normal(this), if_generator(this), if_async(this),
44       if_class_constructor(this), if_function_without_prototype(this),
45       load_map(this);
46   Variable map_index(this, MachineType::PointerRepresentation());
47 
48   STATIC_ASSERT(FunctionKind::kNormalFunction == 0);
49   Node* is_not_normal =
50       Word32And(compiler_hints,
51                 Int32Constant(SharedFunctionInfo::kAllFunctionKindBitsMask));
52   GotoIfNot(is_not_normal, &if_normal);
53 
54   Node* is_generator = Word32And(
55       compiler_hints, Int32Constant(FunctionKind::kGeneratorFunction
56                                     << SharedFunctionInfo::kFunctionKindShift));
57   GotoIf(is_generator, &if_generator);
58 
59   Node* is_async = Word32And(
60       compiler_hints, Int32Constant(FunctionKind::kAsyncFunction
61                                     << SharedFunctionInfo::kFunctionKindShift));
62   GotoIf(is_async, &if_async);
63 
64   Node* is_class_constructor = Word32And(
65       compiler_hints, Int32Constant(FunctionKind::kClassConstructor
66                                     << SharedFunctionInfo::kFunctionKindShift));
67   GotoIf(is_class_constructor, &if_class_constructor);
68 
69   if (FLAG_debug_code) {
70     // Function must be a function without a prototype.
71     CSA_ASSERT(
72         this,
73         Word32And(compiler_hints,
74                   Int32Constant((FunctionKind::kAccessorFunction |
75                                  FunctionKind::kArrowFunction |
76                                  FunctionKind::kConciseMethod)
77                                 << SharedFunctionInfo::kFunctionKindShift)));
78   }
79   Goto(&if_function_without_prototype);
80 
81   Bind(&if_normal);
82   {
83     map_index.Bind(SelectIntPtrConstant(is_strict,
84                                         Context::STRICT_FUNCTION_MAP_INDEX,
85                                         Context::SLOPPY_FUNCTION_MAP_INDEX));
86     Goto(&load_map);
87   }
88 
89   Bind(&if_generator);
90   {
91     map_index.Bind(IntPtrConstant(Context::GENERATOR_FUNCTION_MAP_INDEX));
92     Goto(&load_map);
93   }
94 
95   Bind(&if_async);
96   {
97     map_index.Bind(IntPtrConstant(Context::ASYNC_FUNCTION_MAP_INDEX));
98     Goto(&load_map);
99   }
100 
101   Bind(&if_class_constructor);
102   {
103     map_index.Bind(IntPtrConstant(Context::CLASS_FUNCTION_MAP_INDEX));
104     Goto(&load_map);
105   }
106 
107   Bind(&if_function_without_prototype);
108   {
109     map_index.Bind(
110         IntPtrConstant(Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
111     Goto(&load_map);
112   }
113 
114   Bind(&load_map);
115 
116   // Get the function map in the current native context and set that
117   // as the map of the allocated object.
118   Node* native_context = LoadNativeContext(context);
119   Node* map_slot_value =
120       LoadFixedArrayElement(native_context, map_index.value());
121   StoreMapNoWriteBarrier(result, map_slot_value);
122 
123   // Initialize the rest of the function.
124   Node* empty_fixed_array = HeapConstant(factory->empty_fixed_array());
125   StoreObjectFieldNoWriteBarrier(result, JSObject::kPropertiesOffset,
126                                  empty_fixed_array);
127   StoreObjectFieldNoWriteBarrier(result, JSObject::kElementsOffset,
128                                  empty_fixed_array);
129   Node* literals_cell = LoadFixedArrayElement(
130       feedback_vector, slot, 0, CodeStubAssembler::SMI_PARAMETERS);
131   {
132     // Bump the closure counter encoded in the cell's map.
133     Node* cell_map = LoadMap(literals_cell);
134     Label no_closures(this), one_closure(this), cell_done(this);
135 
136     GotoIf(IsNoClosuresCellMap(cell_map), &no_closures);
137     GotoIf(IsOneClosureCellMap(cell_map), &one_closure);
138     CSA_ASSERT(this, IsManyClosuresCellMap(cell_map));
139     Goto(&cell_done);
140 
141     Bind(&no_closures);
142     StoreMapNoWriteBarrier(literals_cell, Heap::kOneClosureCellMapRootIndex);
143     Goto(&cell_done);
144 
145     Bind(&one_closure);
146     StoreMapNoWriteBarrier(literals_cell, Heap::kManyClosuresCellMapRootIndex);
147     Goto(&cell_done);
148 
149     Bind(&cell_done);
150   }
151   StoreObjectFieldNoWriteBarrier(result, JSFunction::kFeedbackVectorOffset,
152                                  literals_cell);
153   StoreObjectFieldNoWriteBarrier(
154       result, JSFunction::kPrototypeOrInitialMapOffset, TheHoleConstant());
155   StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset,
156                                  shared_info);
157   StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context);
158   Handle<Code> lazy_builtin_handle(
159       isolate->builtins()->builtin(Builtins::kCompileLazy));
160   Node* lazy_builtin = HeapConstant(lazy_builtin_handle);
161   Node* lazy_builtin_entry =
162       IntPtrAdd(BitcastTaggedToWord(lazy_builtin),
163                 IntPtrConstant(Code::kHeaderSize - kHeapObjectTag));
164   StoreObjectFieldNoWriteBarrier(result, JSFunction::kCodeEntryOffset,
165                                  lazy_builtin_entry,
166                                  MachineType::PointerRepresentation());
167   StoreObjectFieldNoWriteBarrier(result, JSFunction::kNextFunctionLinkOffset,
168                                  UndefinedConstant());
169 
170   return result;
171 }
172 
TF_BUILTIN(FastNewClosure,ConstructorBuiltinsAssembler)173 TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) {
174   Node* shared = Parameter(FastNewClosureDescriptor::kSharedFunctionInfo);
175   Node* context = Parameter(FastNewClosureDescriptor::kContext);
176   Node* vector = Parameter(FastNewClosureDescriptor::kVector);
177   Node* slot = Parameter(FastNewClosureDescriptor::kSlot);
178   Return(EmitFastNewClosure(shared, vector, slot, context));
179 }
180 
TF_BUILTIN(FastNewObject,ConstructorBuiltinsAssembler)181 TF_BUILTIN(FastNewObject, ConstructorBuiltinsAssembler) {
182   typedef FastNewObjectDescriptor Descriptor;
183   Node* context = Parameter(Descriptor::kContext);
184   Node* target = Parameter(Descriptor::kTarget);
185   Node* new_target = Parameter(Descriptor::kNewTarget);
186 
187   Label call_runtime(this);
188 
189   Node* result = EmitFastNewObject(context, target, new_target, &call_runtime);
190   Return(result);
191 
192   Bind(&call_runtime);
193   TailCallRuntime(Runtime::kNewObject, context, target, new_target);
194 }
195 
EmitFastNewObject(Node * context,Node * target,Node * new_target)196 Node* ConstructorBuiltinsAssembler::EmitFastNewObject(Node* context,
197                                                       Node* target,
198                                                       Node* new_target) {
199   Variable var_obj(this, MachineRepresentation::kTagged);
200   Label call_runtime(this), end(this);
201 
202   Node* result = EmitFastNewObject(context, target, new_target, &call_runtime);
203   var_obj.Bind(result);
204   Goto(&end);
205 
206   Bind(&call_runtime);
207   var_obj.Bind(CallRuntime(Runtime::kNewObject, context, target, new_target));
208   Goto(&end);
209 
210   Bind(&end);
211   return var_obj.value();
212 }
213 
EmitFastNewObject(Node * context,Node * target,Node * new_target,CodeAssemblerLabel * call_runtime)214 Node* ConstructorBuiltinsAssembler::EmitFastNewObject(
215     Node* context, Node* target, Node* new_target,
216     CodeAssemblerLabel* call_runtime) {
217   CSA_ASSERT(this, HasInstanceType(target, JS_FUNCTION_TYPE));
218   CSA_ASSERT(this, IsJSReceiver(new_target));
219 
220   // Verify that the new target is a JSFunction.
221   Label fast(this), end(this);
222   GotoIf(HasInstanceType(new_target, JS_FUNCTION_TYPE), &fast);
223   Goto(call_runtime);
224 
225   Bind(&fast);
226 
227   // Load the initial map and verify that it's in fact a map.
228   Node* initial_map =
229       LoadObjectField(new_target, JSFunction::kPrototypeOrInitialMapOffset);
230   GotoIf(TaggedIsSmi(initial_map), call_runtime);
231   GotoIf(DoesntHaveInstanceType(initial_map, MAP_TYPE), call_runtime);
232 
233   // Fall back to runtime if the target differs from the new target's
234   // initial map constructor.
235   Node* new_target_constructor =
236       LoadObjectField(initial_map, Map::kConstructorOrBackPointerOffset);
237   GotoIf(WordNotEqual(target, new_target_constructor), call_runtime);
238 
239   Node* instance_size_words = ChangeUint32ToWord(LoadObjectField(
240       initial_map, Map::kInstanceSizeOffset, MachineType::Uint8()));
241   Node* instance_size =
242       WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2));
243 
244   Node* object = Allocate(instance_size);
245   StoreMapNoWriteBarrier(object, initial_map);
246   Node* empty_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
247   StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOffset,
248                                  empty_array);
249   StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset,
250                                  empty_array);
251 
252   instance_size_words = ChangeUint32ToWord(LoadObjectField(
253       initial_map, Map::kInstanceSizeOffset, MachineType::Uint8()));
254   instance_size =
255       WordShl(instance_size_words, IntPtrConstant(kPointerSizeLog2));
256 
257   // Perform in-object slack tracking if requested.
258   Node* bit_field3 = LoadMapBitField3(initial_map);
259   Label slack_tracking(this), finalize(this, Label::kDeferred), done(this);
260   GotoIf(IsSetWord32<Map::ConstructionCounter>(bit_field3), &slack_tracking);
261 
262   // Initialize remaining fields.
263   {
264     Comment("no slack tracking");
265     InitializeFieldsWithRoot(object, IntPtrConstant(JSObject::kHeaderSize),
266                              instance_size, Heap::kUndefinedValueRootIndex);
267     Goto(&end);
268   }
269 
270   {
271     Bind(&slack_tracking);
272 
273     // Decrease generous allocation count.
274     STATIC_ASSERT(Map::ConstructionCounter::kNext == 32);
275     Comment("update allocation count");
276     Node* new_bit_field3 = Int32Sub(
277         bit_field3, Int32Constant(1 << Map::ConstructionCounter::kShift));
278     StoreObjectFieldNoWriteBarrier(initial_map, Map::kBitField3Offset,
279                                    new_bit_field3,
280                                    MachineRepresentation::kWord32);
281     GotoIf(IsClearWord32<Map::ConstructionCounter>(new_bit_field3), &finalize);
282 
283     Node* unused_fields = LoadObjectField(
284         initial_map, Map::kUnusedPropertyFieldsOffset, MachineType::Uint8());
285     Node* used_size =
286         IntPtrSub(instance_size, WordShl(ChangeUint32ToWord(unused_fields),
287                                          IntPtrConstant(kPointerSizeLog2)));
288 
289     Comment("initialize filler fields (no finalize)");
290     InitializeFieldsWithRoot(object, used_size, instance_size,
291                              Heap::kOnePointerFillerMapRootIndex);
292 
293     Comment("initialize undefined fields (no finalize)");
294     InitializeFieldsWithRoot(object, IntPtrConstant(JSObject::kHeaderSize),
295                              used_size, Heap::kUndefinedValueRootIndex);
296     Goto(&end);
297   }
298 
299   {
300     // Finalize the instance size.
301     Bind(&finalize);
302 
303     Node* unused_fields = LoadObjectField(
304         initial_map, Map::kUnusedPropertyFieldsOffset, MachineType::Uint8());
305     Node* used_size =
306         IntPtrSub(instance_size, WordShl(ChangeUint32ToWord(unused_fields),
307                                          IntPtrConstant(kPointerSizeLog2)));
308 
309     Comment("initialize filler fields (finalize)");
310     InitializeFieldsWithRoot(object, used_size, instance_size,
311                              Heap::kOnePointerFillerMapRootIndex);
312 
313     Comment("initialize undefined fields (finalize)");
314     InitializeFieldsWithRoot(object, IntPtrConstant(JSObject::kHeaderSize),
315                              used_size, Heap::kUndefinedValueRootIndex);
316 
317     CallRuntime(Runtime::kFinalizeInstanceSize, context, initial_map);
318     Goto(&end);
319   }
320 
321   Bind(&end);
322   return object;
323 }
324 
EmitFastNewFunctionContext(Node * function,Node * slots,Node * context,ScopeType scope_type)325 Node* ConstructorBuiltinsAssembler::EmitFastNewFunctionContext(
326     Node* function, Node* slots, Node* context, ScopeType scope_type) {
327   slots = ChangeUint32ToWord(slots);
328 
329   // TODO(ishell): Use CSA::OptimalParameterMode() here.
330   CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
331   Node* min_context_slots = IntPtrConstant(Context::MIN_CONTEXT_SLOTS);
332   Node* length = IntPtrAdd(slots, min_context_slots);
333   Node* size = GetFixedArrayAllocationSize(length, FAST_ELEMENTS, mode);
334 
335   // Create a new closure from the given function info in new space
336   Node* function_context = Allocate(size);
337 
338   Heap::RootListIndex context_type;
339   switch (scope_type) {
340     case EVAL_SCOPE:
341       context_type = Heap::kEvalContextMapRootIndex;
342       break;
343     case FUNCTION_SCOPE:
344       context_type = Heap::kFunctionContextMapRootIndex;
345       break;
346     default:
347       UNREACHABLE();
348   }
349   StoreMapNoWriteBarrier(function_context, context_type);
350   StoreObjectFieldNoWriteBarrier(function_context, Context::kLengthOffset,
351                                  SmiTag(length));
352 
353   // Set up the fixed slots.
354   StoreFixedArrayElement(function_context, Context::CLOSURE_INDEX, function,
355                          SKIP_WRITE_BARRIER);
356   StoreFixedArrayElement(function_context, Context::PREVIOUS_INDEX, context,
357                          SKIP_WRITE_BARRIER);
358   StoreFixedArrayElement(function_context, Context::EXTENSION_INDEX,
359                          TheHoleConstant(), SKIP_WRITE_BARRIER);
360 
361   // Copy the native context from the previous context.
362   Node* native_context = LoadNativeContext(context);
363   StoreFixedArrayElement(function_context, Context::NATIVE_CONTEXT_INDEX,
364                          native_context, SKIP_WRITE_BARRIER);
365 
366   // Initialize the rest of the slots to undefined.
367   Node* undefined = UndefinedConstant();
368   BuildFastFixedArrayForEach(
369       function_context, FAST_ELEMENTS, min_context_slots, length,
370       [this, undefined](Node* context, Node* offset) {
371         StoreNoWriteBarrier(MachineRepresentation::kTagged, context, offset,
372                             undefined);
373       },
374       mode);
375 
376   return function_context;
377 }
378 
379 // static
MaximumFunctionContextSlots()380 int ConstructorBuiltinsAssembler::MaximumFunctionContextSlots() {
381   return FLAG_test_small_max_function_context_stub_size ? kSmallMaximumSlots
382                                                         : kMaximumSlots;
383 }
384 
TF_BUILTIN(FastNewFunctionContextEval,ConstructorBuiltinsAssembler)385 TF_BUILTIN(FastNewFunctionContextEval, ConstructorBuiltinsAssembler) {
386   Node* function = Parameter(FastNewFunctionContextDescriptor::kFunction);
387   Node* slots = Parameter(FastNewFunctionContextDescriptor::kSlots);
388   Node* context = Parameter(FastNewFunctionContextDescriptor::kContext);
389   Return(EmitFastNewFunctionContext(function, slots, context,
390                                     ScopeType::EVAL_SCOPE));
391 }
392 
TF_BUILTIN(FastNewFunctionContextFunction,ConstructorBuiltinsAssembler)393 TF_BUILTIN(FastNewFunctionContextFunction, ConstructorBuiltinsAssembler) {
394   Node* function = Parameter(FastNewFunctionContextDescriptor::kFunction);
395   Node* slots = Parameter(FastNewFunctionContextDescriptor::kSlots);
396   Node* context = Parameter(FastNewFunctionContextDescriptor::kContext);
397   Return(EmitFastNewFunctionContext(function, slots, context,
398                                     ScopeType::FUNCTION_SCOPE));
399 }
400 
NewFunctionContext(ScopeType scope_type)401 Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
402   switch (scope_type) {
403     case ScopeType::EVAL_SCOPE:
404       return FastNewFunctionContextEval();
405     case ScopeType::FUNCTION_SCOPE:
406       return FastNewFunctionContextFunction();
407     default:
408       UNREACHABLE();
409   }
410   return Handle<Code>::null();
411 }
412 
EmitFastCloneRegExp(Node * closure,Node * literal_index,Node * pattern,Node * flags,Node * context)413 Node* ConstructorBuiltinsAssembler::EmitFastCloneRegExp(Node* closure,
414                                                         Node* literal_index,
415                                                         Node* pattern,
416                                                         Node* flags,
417                                                         Node* context) {
418   typedef CodeStubAssembler::Label Label;
419   typedef CodeStubAssembler::Variable Variable;
420   typedef compiler::Node Node;
421 
422   Label call_runtime(this, Label::kDeferred), end(this);
423 
424   Variable result(this, MachineRepresentation::kTagged);
425 
426   Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset);
427   Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset);
428   Node* boilerplate = LoadFixedArrayElement(feedback_vector, literal_index, 0,
429                                             CodeStubAssembler::SMI_PARAMETERS);
430   GotoIf(IsUndefined(boilerplate), &call_runtime);
431 
432   {
433     int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
434     Node* copy = Allocate(size);
435     for (int offset = 0; offset < size; offset += kPointerSize) {
436       Node* value = LoadObjectField(boilerplate, offset);
437       StoreObjectFieldNoWriteBarrier(copy, offset, value);
438     }
439     result.Bind(copy);
440     Goto(&end);
441   }
442 
443   Bind(&call_runtime);
444   {
445     result.Bind(CallRuntime(Runtime::kCreateRegExpLiteral, context, closure,
446                             literal_index, pattern, flags));
447     Goto(&end);
448   }
449 
450   Bind(&end);
451   return result.value();
452 }
453 
TF_BUILTIN(FastCloneRegExp,ConstructorBuiltinsAssembler)454 TF_BUILTIN(FastCloneRegExp, ConstructorBuiltinsAssembler) {
455   Node* closure = Parameter(FastCloneRegExpDescriptor::kClosure);
456   Node* literal_index = Parameter(FastCloneRegExpDescriptor::kLiteralIndex);
457   Node* pattern = Parameter(FastCloneRegExpDescriptor::kPattern);
458   Node* flags = Parameter(FastCloneRegExpDescriptor::kFlags);
459   Node* context = Parameter(FastCloneRegExpDescriptor::kContext);
460 
461   Return(EmitFastCloneRegExp(closure, literal_index, pattern, flags, context));
462 }
463 
NonEmptyShallowClone(Node * boilerplate,Node * boilerplate_map,Node * boilerplate_elements,Node * allocation_site,Node * capacity,ElementsKind kind)464 Node* ConstructorBuiltinsAssembler::NonEmptyShallowClone(
465     Node* boilerplate, Node* boilerplate_map, Node* boilerplate_elements,
466     Node* allocation_site, Node* capacity, ElementsKind kind) {
467   typedef CodeStubAssembler::ParameterMode ParameterMode;
468 
469   ParameterMode param_mode = OptimalParameterMode();
470 
471   Node* length = LoadJSArrayLength(boilerplate);
472   capacity = TaggedToParameter(capacity, param_mode);
473 
474   Node *array, *elements;
475   std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
476       kind, boilerplate_map, length, allocation_site, capacity, param_mode);
477 
478   Comment("copy elements header");
479   // Header consists of map and length.
480   STATIC_ASSERT(FixedArrayBase::kHeaderSize == 2 * kPointerSize);
481   StoreMap(elements, LoadMap(boilerplate_elements));
482   {
483     int offset = FixedArrayBase::kLengthOffset;
484     StoreObjectFieldNoWriteBarrier(
485         elements, offset, LoadObjectField(boilerplate_elements, offset));
486   }
487 
488   length = TaggedToParameter(length, param_mode);
489 
490   Comment("copy boilerplate elements");
491   CopyFixedArrayElements(kind, boilerplate_elements, elements, length,
492                          SKIP_WRITE_BARRIER, param_mode);
493   IncrementCounter(isolate()->counters()->inlined_copied_elements(), 1);
494 
495   return array;
496 }
497 
EmitFastCloneShallowArray(Node * closure,Node * literal_index,Node * context,CodeAssemblerLabel * call_runtime,AllocationSiteMode allocation_site_mode)498 Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowArray(
499     Node* closure, Node* literal_index, Node* context,
500     CodeAssemblerLabel* call_runtime, AllocationSiteMode allocation_site_mode) {
501   typedef CodeStubAssembler::Label Label;
502   typedef CodeStubAssembler::Variable Variable;
503   typedef compiler::Node Node;
504 
505   Label zero_capacity(this), cow_elements(this), fast_elements(this),
506       return_result(this);
507   Variable result(this, MachineRepresentation::kTagged);
508 
509   Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset);
510   Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset);
511   Node* allocation_site = LoadFixedArrayElement(
512       feedback_vector, literal_index, 0, CodeStubAssembler::SMI_PARAMETERS);
513 
514   GotoIf(IsUndefined(allocation_site), call_runtime);
515   allocation_site = LoadFixedArrayElement(feedback_vector, literal_index, 0,
516                                           CodeStubAssembler::SMI_PARAMETERS);
517 
518   Node* boilerplate =
519       LoadObjectField(allocation_site, AllocationSite::kTransitionInfoOffset);
520   Node* boilerplate_map = LoadMap(boilerplate);
521   Node* boilerplate_elements = LoadElements(boilerplate);
522   Node* capacity = LoadFixedArrayBaseLength(boilerplate_elements);
523   allocation_site =
524       allocation_site_mode == TRACK_ALLOCATION_SITE ? allocation_site : nullptr;
525 
526   Node* zero = SmiConstant(Smi::kZero);
527   GotoIf(SmiEqual(capacity, zero), &zero_capacity);
528 
529   Node* elements_map = LoadMap(boilerplate_elements);
530   GotoIf(IsFixedCOWArrayMap(elements_map), &cow_elements);
531 
532   GotoIf(IsFixedArrayMap(elements_map), &fast_elements);
533   {
534     Comment("fast double elements path");
535     if (FLAG_debug_code) {
536       Label correct_elements_map(this), abort(this, Label::kDeferred);
537       Branch(IsFixedDoubleArrayMap(elements_map), &correct_elements_map,
538              &abort);
539 
540       Bind(&abort);
541       {
542         Node* abort_id = SmiConstant(
543             Smi::FromInt(BailoutReason::kExpectedFixedDoubleArrayMap));
544         CallRuntime(Runtime::kAbort, context, abort_id);
545         result.Bind(UndefinedConstant());
546         Goto(&return_result);
547       }
548       Bind(&correct_elements_map);
549     }
550 
551     Node* array =
552         NonEmptyShallowClone(boilerplate, boilerplate_map, boilerplate_elements,
553                              allocation_site, capacity, FAST_DOUBLE_ELEMENTS);
554     result.Bind(array);
555     Goto(&return_result);
556   }
557 
558   Bind(&fast_elements);
559   {
560     Comment("fast elements path");
561     Node* array =
562         NonEmptyShallowClone(boilerplate, boilerplate_map, boilerplate_elements,
563                              allocation_site, capacity, FAST_ELEMENTS);
564     result.Bind(array);
565     Goto(&return_result);
566   }
567 
568   Variable length(this, MachineRepresentation::kTagged),
569       elements(this, MachineRepresentation::kTagged);
570   Label allocate_without_elements(this);
571 
572   Bind(&cow_elements);
573   {
574     Comment("fixed cow path");
575     length.Bind(LoadJSArrayLength(boilerplate));
576     elements.Bind(boilerplate_elements);
577 
578     Goto(&allocate_without_elements);
579   }
580 
581   Bind(&zero_capacity);
582   {
583     Comment("zero capacity path");
584     length.Bind(zero);
585     elements.Bind(LoadRoot(Heap::kEmptyFixedArrayRootIndex));
586 
587     Goto(&allocate_without_elements);
588   }
589 
590   Bind(&allocate_without_elements);
591   {
592     Node* array = AllocateUninitializedJSArrayWithoutElements(
593         FAST_ELEMENTS, boilerplate_map, length.value(), allocation_site);
594     StoreObjectField(array, JSObject::kElementsOffset, elements.value());
595     result.Bind(array);
596     Goto(&return_result);
597   }
598 
599   Bind(&return_result);
600   return result.value();
601 }
602 
CreateFastCloneShallowArrayBuiltin(AllocationSiteMode allocation_site_mode)603 void ConstructorBuiltinsAssembler::CreateFastCloneShallowArrayBuiltin(
604     AllocationSiteMode allocation_site_mode) {
605   typedef compiler::Node Node;
606   typedef CodeStubAssembler::Label Label;
607 
608   Node* closure = Parameter(FastCloneShallowArrayDescriptor::kClosure);
609   Node* literal_index =
610       Parameter(FastCloneShallowArrayDescriptor::kLiteralIndex);
611   Node* constant_elements =
612       Parameter(FastCloneShallowArrayDescriptor::kConstantElements);
613   Node* context = Parameter(FastCloneShallowArrayDescriptor::kContext);
614   Label call_runtime(this, Label::kDeferred);
615   Return(EmitFastCloneShallowArray(closure, literal_index, context,
616                                    &call_runtime, allocation_site_mode));
617 
618   Bind(&call_runtime);
619   {
620     Comment("call runtime");
621     Node* flags =
622         SmiConstant(Smi::FromInt(ArrayLiteral::kShallowElements |
623                                  (allocation_site_mode == TRACK_ALLOCATION_SITE
624                                       ? 0
625                                       : ArrayLiteral::kDisableMementos)));
626     Return(CallRuntime(Runtime::kCreateArrayLiteral, context, closure,
627                        literal_index, constant_elements, flags));
628   }
629 }
630 
TF_BUILTIN(FastCloneShallowArrayTrack,ConstructorBuiltinsAssembler)631 TF_BUILTIN(FastCloneShallowArrayTrack, ConstructorBuiltinsAssembler) {
632   CreateFastCloneShallowArrayBuiltin(TRACK_ALLOCATION_SITE);
633 }
634 
TF_BUILTIN(FastCloneShallowArrayDontTrack,ConstructorBuiltinsAssembler)635 TF_BUILTIN(FastCloneShallowArrayDontTrack, ConstructorBuiltinsAssembler) {
636   CreateFastCloneShallowArrayBuiltin(DONT_TRACK_ALLOCATION_SITE);
637 }
638 
NewCloneShallowArray(AllocationSiteMode allocation_mode)639 Handle<Code> Builtins::NewCloneShallowArray(
640     AllocationSiteMode allocation_mode) {
641   switch (allocation_mode) {
642     case TRACK_ALLOCATION_SITE:
643       return FastCloneShallowArrayTrack();
644     case DONT_TRACK_ALLOCATION_SITE:
645       return FastCloneShallowArrayDontTrack();
646     default:
647       UNREACHABLE();
648   }
649   return Handle<Code>::null();
650 }
651 
652 // static
FastCloneShallowObjectPropertiesCount(int literal_length)653 int ConstructorBuiltinsAssembler::FastCloneShallowObjectPropertiesCount(
654     int literal_length) {
655   // This heuristic of setting empty literals to have
656   // kInitialGlobalObjectUnusedPropertiesCount must remain in-sync with the
657   // runtime.
658   // TODO(verwaest): Unify this with the heuristic in the runtime.
659   return literal_length == 0
660              ? JSObject::kInitialGlobalObjectUnusedPropertiesCount
661              : literal_length;
662 }
663 
EmitFastCloneShallowObject(CodeAssemblerLabel * call_runtime,Node * closure,Node * literals_index,Node * properties_count)664 Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
665     CodeAssemblerLabel* call_runtime, Node* closure, Node* literals_index,
666     Node* properties_count) {
667   Node* cell = LoadObjectField(closure, JSFunction::kFeedbackVectorOffset);
668   Node* feedback_vector = LoadObjectField(cell, Cell::kValueOffset);
669   Node* allocation_site = LoadFixedArrayElement(
670       feedback_vector, literals_index, 0, CodeStubAssembler::SMI_PARAMETERS);
671   GotoIf(IsUndefined(allocation_site), call_runtime);
672 
673   // Calculate the object and allocation size based on the properties count.
674   Node* object_size = IntPtrAdd(WordShl(properties_count, kPointerSizeLog2),
675                                 IntPtrConstant(JSObject::kHeaderSize));
676   Node* allocation_size = object_size;
677   if (FLAG_allocation_site_pretenuring) {
678     allocation_size =
679         IntPtrAdd(object_size, IntPtrConstant(AllocationMemento::kSize));
680   }
681   Node* boilerplate =
682       LoadObjectField(allocation_site, AllocationSite::kTransitionInfoOffset);
683   Node* boilerplate_map = LoadMap(boilerplate);
684   Node* instance_size = LoadMapInstanceSize(boilerplate_map);
685   Node* size_in_words = WordShr(object_size, kPointerSizeLog2);
686   GotoIfNot(WordEqual(instance_size, size_in_words), call_runtime);
687 
688   Node* copy = Allocate(allocation_size);
689 
690   // Copy boilerplate elements.
691   Variable offset(this, MachineType::PointerRepresentation());
692   offset.Bind(IntPtrConstant(-kHeapObjectTag));
693   Node* end_offset = IntPtrAdd(object_size, offset.value());
694   Label loop_body(this, &offset), loop_check(this, &offset);
695   // We should always have an object size greater than zero.
696   Goto(&loop_body);
697   Bind(&loop_body);
698   {
699     // The Allocate above guarantees that the copy lies in new space. This
700     // allows us to skip write barriers. This is necessary since we may also be
701     // copying unboxed doubles.
702     Node* field = Load(MachineType::IntPtr(), boilerplate, offset.value());
703     StoreNoWriteBarrier(MachineType::PointerRepresentation(), copy,
704                         offset.value(), field);
705     Goto(&loop_check);
706   }
707   Bind(&loop_check);
708   {
709     offset.Bind(IntPtrAdd(offset.value(), IntPtrConstant(kPointerSize)));
710     GotoIfNot(IntPtrGreaterThanOrEqual(offset.value(), end_offset), &loop_body);
711   }
712 
713   if (FLAG_allocation_site_pretenuring) {
714     Node* memento = InnerAllocate(copy, object_size);
715     StoreMapNoWriteBarrier(memento, Heap::kAllocationMementoMapRootIndex);
716     StoreObjectFieldNoWriteBarrier(
717         memento, AllocationMemento::kAllocationSiteOffset, allocation_site);
718     Node* memento_create_count = LoadObjectField(
719         allocation_site, AllocationSite::kPretenureCreateCountOffset);
720     memento_create_count =
721         SmiAdd(memento_create_count, SmiConstant(Smi::FromInt(1)));
722     StoreObjectFieldNoWriteBarrier(allocation_site,
723                                    AllocationSite::kPretenureCreateCountOffset,
724                                    memento_create_count);
725   }
726 
727   // TODO(verwaest): Allocate and fill in double boxes.
728   return copy;
729 }
730 
CreateFastCloneShallowObjectBuiltin(int properties_count)731 void ConstructorBuiltinsAssembler::CreateFastCloneShallowObjectBuiltin(
732     int properties_count) {
733   DCHECK_GE(properties_count, 0);
734   DCHECK_LE(properties_count, kMaximumClonedShallowObjectProperties);
735   Label call_runtime(this);
736   Node* closure = Parameter(0);
737   Node* literals_index = Parameter(1);
738 
739   Node* properties_count_node =
740       IntPtrConstant(FastCloneShallowObjectPropertiesCount(properties_count));
741   Node* copy = EmitFastCloneShallowObject(
742       &call_runtime, closure, literals_index, properties_count_node);
743   Return(copy);
744 
745   Bind(&call_runtime);
746   Node* constant_properties = Parameter(2);
747   Node* flags = Parameter(3);
748   Node* context = Parameter(4);
749   TailCallRuntime(Runtime::kCreateObjectLiteral, context, closure,
750                   literals_index, constant_properties, flags);
751 }
752 
753 #define SHALLOW_OBJECT_BUILTIN(props)                                       \
754   TF_BUILTIN(FastCloneShallowObject##props, ConstructorBuiltinsAssembler) { \
755     CreateFastCloneShallowObjectBuiltin(props);                             \
756   }
757 
758 SHALLOW_OBJECT_BUILTIN(0);
759 SHALLOW_OBJECT_BUILTIN(1);
760 SHALLOW_OBJECT_BUILTIN(2);
761 SHALLOW_OBJECT_BUILTIN(3);
762 SHALLOW_OBJECT_BUILTIN(4);
763 SHALLOW_OBJECT_BUILTIN(5);
764 SHALLOW_OBJECT_BUILTIN(6);
765 
NewCloneShallowObject(int length)766 Handle<Code> Builtins::NewCloneShallowObject(int length) {
767   switch (length) {
768     case 0:
769       return FastCloneShallowObject0();
770     case 1:
771       return FastCloneShallowObject1();
772     case 2:
773       return FastCloneShallowObject2();
774     case 3:
775       return FastCloneShallowObject3();
776     case 4:
777       return FastCloneShallowObject4();
778     case 5:
779       return FastCloneShallowObject5();
780     case 6:
781       return FastCloneShallowObject6();
782     default:
783       UNREACHABLE();
784   }
785   return Handle<Code>::null();
786 }
787 
788 }  // namespace internal
789 }  // namespace v8
790