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