Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: src/builtins/builtins-array.cc

Issue 2663033003: [builtins] TurboFan version of Array.prototype.forEach including fast path for FAST_ELEMENTS (Closed)
Patch Set: Review feedback Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins.h ('k') | src/flag-definitions.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/builtins/builtins.h" 5 #include "src/builtins/builtins.h"
6 #include "src/builtins/builtins-utils.h" 6 #include "src/builtins/builtins-utils.h"
7 7
8 #include "src/code-factory.h" 8 #include "src/code-factory.h"
9 #include "src/code-stub-assembler.h" 9 #include "src/code-stub-assembler.h"
10 #include "src/contexts.h" 10 #include "src/contexts.h"
(...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 419
420 if (JSArray::HasReadOnlyLength(array)) { 420 if (JSArray::HasReadOnlyLength(array)) {
421 return CallJsIntrinsic(isolate, isolate->array_unshift(), args); 421 return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
422 } 422 }
423 423
424 ElementsAccessor* accessor = array->GetElementsAccessor(); 424 ElementsAccessor* accessor = array->GetElementsAccessor();
425 int new_length = accessor->Unshift(array, &args, to_add); 425 int new_length = accessor->Unshift(array, &args, to_add);
426 return Smi::FromInt(new_length); 426 return Smi::FromInt(new_length);
427 } 427 }
428 428
429 class ForEachCodeStubAssembler : public CodeStubAssembler {
430 public:
431 explicit ForEachCodeStubAssembler(compiler::CodeAssemblerState* state)
432 : CodeStubAssembler(state) {}
433
434 void VisitOneElement(Node* context, Node* this_arg, Node* o, Node* k,
435 Node* callbackfn) {
436 Comment("begin VisitOneElement");
437
438 // a. Let Pk be ToString(k).
439 Node* p_k = ToString(context, k);
440
441 // b. Let kPresent be HasProperty(O, Pk).
442 // c. ReturnIfAbrupt(kPresent).
443 Node* k_present =
444 CallStub(CodeFactory::HasProperty(isolate()), context, p_k, o);
445
446 // d. If kPresent is true, then
447 Label not_present(this);
448 GotoIf(WordNotEqual(k_present, TrueConstant()), &not_present);
449
450 // i. Let kValue be Get(O, Pk).
451 // ii. ReturnIfAbrupt(kValue).
452 Node* k_value =
453 CallStub(CodeFactory::GetProperty(isolate()), context, o, k);
454
455 // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
456 // iv. ReturnIfAbrupt(funcResult).
457 CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg, k_value,
458 k, o);
459
460 Goto(&not_present);
461 Bind(&not_present);
462 Comment("end VisitOneElement");
463 }
464
465 void VisitAllFastElements(Node* context, ElementsKind kind, Node* this_arg,
466 Node* o, Node* len, Node* callbackfn,
467 ParameterMode mode) {
468 Comment("begin VisitAllFastElements");
469 Variable original_map(this, MachineRepresentation::kTagged);
470 original_map.Bind(LoadMap(o));
471 VariableList list({&original_map}, zone());
472 BuildFastLoop(
473 list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode),
474 [context, kind, this, o, &original_map, callbackfn, this_arg,
475 mode](Node* index) {
476 Label one_element_done(this), array_changed(this, Label::kDeferred),
477 hole_element(this);
478
479 // Check if o's map has changed during the callback. If so, we have to
480 // fall back to the slower spec implementation for the rest of the
481 // iteration.
482 Node* o_map = LoadMap(o);
483 GotoIf(WordNotEqual(o_map, original_map.value()), &array_changed);
484
485 // Check if o's length has changed during the callback and if the
486 // index is now out of range of the new length.
487 Node* tagged_index = ParameterToTagged(index, mode);
488 GotoIf(SmiGreaterThanOrEqual(tagged_index, LoadJSArrayLength(o)),
489 &array_changed);
490
491 // Re-load the elements array. If may have been resized.
492 Node* elements = LoadElements(o);
493
494 // Fast case: load the element directly from the elements FixedArray
495 // and call the callback if the element is not the hole.
496 DCHECK(kind == FAST_ELEMENTS || kind == FAST_DOUBLE_ELEMENTS);
497 int base_size = kind == FAST_ELEMENTS
498 ? FixedArray::kHeaderSize
499 : (FixedArray::kHeaderSize - kHeapObjectTag);
500 Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size);
501 Node* value = nullptr;
502 if (kind == FAST_ELEMENTS) {
503 value = LoadObjectField(elements, offset);
504 GotoIf(WordEqual(value, TheHoleConstant()), &hole_element);
505 } else {
506 Node* double_value =
507 LoadDoubleWithHoleCheck(elements, offset, &hole_element);
508 value = AllocateHeapNumberWithValue(double_value);
509 }
510 CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
511 value, tagged_index, o);
512 Goto(&one_element_done);
513
514 Bind(&hole_element);
515 BranchIfPrototypesHaveNoElements(o_map, &one_element_done,
516 &array_changed);
517
518 // O's changed during the forEach. Use the implementation precisely
519 // specified in the spec for the rest of the iteration, also making
520 // the failed original_map sticky in case of a subseuent change that
521 // goes back to the original map.
522 Bind(&array_changed);
523 VisitOneElement(context, this_arg, o, ParameterToTagged(index, mode),
524 callbackfn);
525 original_map.Bind(UndefinedConstant());
526 Goto(&one_element_done);
527
528 Bind(&one_element_done);
529 },
530 1, mode, IndexAdvanceMode::kPost);
531 Comment("end VisitAllFastElements");
532 }
533 };
534
535 TF_BUILTIN(ArrayForEach, ForEachCodeStubAssembler) {
536 Label non_array(this), examine_elements(this), fast_elements(this),
537 slow(this), maybe_double_elements(this), fast_double_elements(this);
538
539 Node* receiver = Parameter(ForEachDescriptor::kReceiver);
540 Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
541 Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
542 Node* context = Parameter(ForEachDescriptor::kContext);
543
544 // TODO(danno): Seriously? Do we really need to throw the exact error message
545 // on null and undefined so that the webkit tests pass?
546 Label throw_null_undefined_exception(this, Label::kDeferred);
547 GotoIf(WordEqual(receiver, NullConstant()), &throw_null_undefined_exception);
548 GotoIf(WordEqual(receiver, UndefinedConstant()),
549 &throw_null_undefined_exception);
550
551 // By the book: taken directly from the ECMAScript 2015 specification
552
553 // 1. Let O be ToObject(this value).
554 // 2. ReturnIfAbrupt(O)
555 Node* o = CallStub(CodeFactory::ToObject(isolate()), context, receiver);
556
557 // 3. Let len be ToLength(Get(O, "length")).
558 // 4. ReturnIfAbrupt(len).
559 Variable merged_length(this, MachineRepresentation::kTagged);
560 Label has_length(this, &merged_length), not_js_array(this);
561 GotoIf(DoesntHaveInstanceType(o, JS_ARRAY_TYPE), &not_js_array);
562 merged_length.Bind(LoadJSArrayLength(o));
563 Goto(&has_length);
564 Bind(&not_js_array);
565 Node* len_property =
566 CallStub(CodeFactory::GetProperty(isolate()), context, o,
567 HeapConstant(isolate()->factory()->length_string()));
568 merged_length.Bind(
569 CallStub(CodeFactory::ToLength(isolate()), context, len_property));
570 Goto(&has_length);
571 Bind(&has_length);
572 Node* len = merged_length.value();
573
574 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
575 Label type_exception(this, Label::kDeferred);
576 GotoIf(TaggedIsSmi(callbackfn), &type_exception);
577 GotoUnless(IsCallableMap(LoadMap(callbackfn)), &type_exception);
578
579 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
580 // [Already done by the arguments adapter]
581
582 // Non-smi lengths must use the slow path.
583 GotoIf(TaggedIsNotSmi(len), &slow);
584
585 BranchIfFastJSArray(o, context,
586 CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ,
587 &examine_elements, &slow);
588
589 Bind(&examine_elements);
590
591 ParameterMode mode = OptimalParameterMode();
592
593 // Select by ElementsKind
594 Node* o_map = LoadMap(o);
595 Node* bit_field2 = LoadMapBitField2(o_map);
596 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
597 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
598 &maybe_double_elements, &fast_elements);
599
600 Bind(&fast_elements);
601 {
602 VisitAllFastElements(context, FAST_ELEMENTS, this_arg, o, len, callbackfn,
603 mode);
604
605 // No exception, return success
606 Return(UndefinedConstant());
607 }
608
609 Bind(&maybe_double_elements);
610 Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)),
611 &slow, &fast_double_elements);
612
613 Bind(&fast_double_elements);
614 {
615 VisitAllFastElements(context, FAST_DOUBLE_ELEMENTS, this_arg, o, len,
616 callbackfn, mode);
617
618 // No exception, return success
619 Return(UndefinedConstant());
620 }
621
622 Bind(&slow);
623 {
624 // By the book: taken from the ECMAScript 2015 specification (cont.)
625
626 // 7. Let k be 0.
627 Variable k(this, MachineRepresentation::kTagged);
628 k.Bind(SmiConstant(0));
629
630 // 8. Repeat, while k < len
631 Label loop(this, &k);
632 Label after_loop(this);
633 Goto(&loop);
634 Bind(&loop);
635 {
636 GotoUnlessNumberLessThan(k.value(), len, &after_loop);
637
638 VisitOneElement(context, this_arg, o, k.value(), callbackfn);
639
640 // e. Increase k by 1.
641 k.Bind(NumberInc(k.value()));
642 Goto(&loop);
643 }
644 Bind(&after_loop);
645 Return(UndefinedConstant());
646 }
647
648 Bind(&throw_null_undefined_exception);
649 {
650 CallRuntime(Runtime::kThrowTypeError, context,
651 SmiConstant(MessageTemplate::kCalledOnNullOrUndefined),
652 HeapConstant(isolate()->factory()->NewStringFromAsciiChecked(
653 "Array.prototype.forEach")));
654 Return(UndefinedConstant());
655 }
656
657 Bind(&type_exception);
658 {
659 CallRuntime(Runtime::kThrowTypeError, context,
660 SmiConstant(MessageTemplate::kCalledNonCallable), callbackfn);
661 Return(UndefinedConstant());
662 }
663 }
664
429 BUILTIN(ArraySlice) { 665 BUILTIN(ArraySlice) {
430 HandleScope scope(isolate); 666 HandleScope scope(isolate);
431 Handle<Object> receiver = args.receiver(); 667 Handle<Object> receiver = args.receiver();
432 int len = -1; 668 int len = -1;
433 int relative_start = 0; 669 int relative_start = 0;
434 int relative_end = 0; 670 int relative_end = 0;
435 671
436 if (receiver->IsJSArray()) { 672 if (receiver->IsJSArray()) {
437 DisallowHeapAllocation no_gc; 673 DisallowHeapAllocation no_gc;
438 JSArray* array = JSArray::cast(*receiver); 674 JSArray* array = JSArray::cast(*receiver);
(...skipping 2333 matching lines...) Expand 10 before | Expand all | Expand 10 after
2772 Node* message = assembler.SmiConstant(MessageTemplate::kDetachedOperation); 3008 Node* message = assembler.SmiConstant(MessageTemplate::kDetachedOperation);
2773 Node* result = 3009 Node* result =
2774 assembler.CallRuntime(Runtime::kThrowTypeError, context, message, 3010 assembler.CallRuntime(Runtime::kThrowTypeError, context, message,
2775 assembler.HeapConstant(operation)); 3011 assembler.HeapConstant(operation));
2776 assembler.Return(result); 3012 assembler.Return(result);
2777 } 3013 }
2778 } 3014 }
2779 3015
2780 } // namespace internal 3016 } // namespace internal
2781 } // namespace v8 3017 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins.h ('k') | src/flag-definitions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698