From 304e74c8b33f3da34c246b959269318e7b05b87a Mon Sep 17 00:00:00 2001 From: Mike Stanton Date: Wed, 23 Jan 2019 16:01:19 +0100 Subject: [PATCH] [Torque] Array.prototype.map implemented in Torque Change-Id: I3a60be25b9c7daadcad6078447348b790b249e1c Reviewed-on: https://chromium-review.googlesource.com/c/1402774 Commit-Queue: Michael Stanton Reviewed-by: Tobias Tebbi Cr-Commit-Position: refs/heads/master@{#59042} --- BUILD.gn | 1 + src/builtins/array-map.tq | 318 ++++++++++++++++++++++++++++ src/builtins/base.tq | 36 +++- src/builtins/builtins-array-gen.cc | 196 ----------------- src/builtins/builtins-definitions.h | 8 - src/code-stub-assembler.h | 14 ++ 6 files changed, 360 insertions(+), 213 deletions(-) create mode 100644 src/builtins/array-map.tq diff --git a/BUILD.gn b/BUILD.gn index 71cbff7e12..2139b77dc6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -865,6 +865,7 @@ torque_files = [ "src/builtins/array-join.tq", "src/builtins/array-lastindexof.tq", "src/builtins/array-of.tq", + "src/builtins/array-map.tq", "src/builtins/array-reverse.tq", "src/builtins/array-slice.tq", "src/builtins/array-splice.tq", diff --git a/src/builtins/array-map.tq b/src/builtins/array-map.tq new file mode 100644 index 0000000000..7e45f44d49 --- /dev/null +++ b/src/builtins/array-map.tq @@ -0,0 +1,318 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +namespace array { + transitioning javascript builtin + ArrayMapLoopEagerDeoptContinuation(implicit context: Context)( + receiver: Object, callback: Object, thisArg: Object, array: Object, + initialK: Object, length: Object): Object { + // All continuation points in the optimized filter implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires Object type for all parameters + // other than {context}. + const jsreceiver: JSReceiver = + Cast(receiver) otherwise unreachable; + const callbackfn: Callable = Cast(callback) otherwise unreachable; + const outputArray: JSReceiver = + Cast(array) otherwise unreachable; + const numberK: Number = Cast(initialK) otherwise unreachable; + const numberLength: Number = Cast(length) otherwise unreachable; + + return ArrayMapLoopContinuation( + jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, + numberLength, Undefined); + } + + transitioning javascript builtin + ArrayMapLoopLazyDeoptContinuation(implicit context: Context)( + receiver: Object, callback: Object, thisArg: Object, array: Object, + initialK: Object, length: Object, result: Object): Object { + // All continuation points in the optimized filter implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver: JSReceiver = + Cast(receiver) otherwise unreachable; + const callbackfn: Callable = Cast(callback) otherwise unreachable; + const outputArray: JSReceiver = + Cast(array) otherwise unreachable; + let numberK: Number = Cast(initialK) otherwise unreachable; + const numberLength: Number = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. map() needs + // to pick up at the next step, which is setting the callback result in + // the output array. After incrementing k, we can glide into the loop + // continuation builtin. + + // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). + CreateDataProperty(outputArray, numberK, result); + + // 7d. Increase k by 1. + numberK = numberK + 1; + + return ArrayMapLoopContinuation( + jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, + numberLength, Undefined); + } + + transitioning builtin ArrayMapLoopContinuation(implicit context: Context)( + receiver: JSReceiver, callbackfn: Callable, thisArg: Object, + array: JSReceiver, o: JSReceiver, initialK: Number, length: Number, + initialTo: Object): Object { + // {initialTo} is ignored. + + // 6. Let k be 0. + // 7. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 7a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. + + // 7b. Let kPresent be ? HasProperty(O, Pk). + const kPresent: Boolean = HasProperty_Inline(o, k); + + // 7c. If kPresent is true, then: + if (kPresent == True) { + // i. Let kValue be ? Get(O, Pk). + const kValue: Object = GetProperty(o, k); + + // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). + const mappedValue: Object = + Call(context, callbackfn, thisArg, kValue, k, o); + + // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). + CreateDataProperty(array, k, mappedValue); + } + + // 7d. Increase k by 1. (done by the loop). + } + + // 8. Return A. + return array; + } + + struct Vector { + constructor(implicit context: Context)(length: Smi) { + this.fixedArray = length > 0 ? + AllocateFixedArrayWithHoles( + SmiUntag(length), kAllowLargeObjectAllocation) : + kEmptyFixedArray; + this.onlySmis = this.onlyNumbers = true; + this.skippedElements = false; + } + + ReportSkippedElement() { + this.skippedElements = true; + } + + CreateJSArray(implicit context: Context)(validLength: Smi): JSArray { + let length: Smi = this.fixedArray.length; + assert(validLength <= length); + let kind: ElementsKind = PACKED_SMI_ELEMENTS; + if (!this.onlySmis) { + if (this.onlyNumbers) { + kind = PACKED_DOUBLE_ELEMENTS; + } else { + kind = PACKED_ELEMENTS; + } + } + + if (this.skippedElements || validLength < length) { + // We also need to create a holey output array if we are + // bailing out of the fast path partway through the array. + // This is indicated by {validLength} < {length}. + // Who knows if the bailout condition will continue to fill in + // every element? + kind = FastHoleyElementsKind(kind); + } + + let map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context)); + let a: JSArray; + + if (IsDoubleElementsKind(kind)) { + // We need to allocate and copy. + // First, initialize the elements field before allocation to prevent + // heap corruption. + const elements: FixedDoubleArray = + AllocateFixedDoubleArrayWithHoles(SmiUntag(length), kNone); + a = new JSArray{map, this.fixedArray}; + for (let i: Smi = 0; i < validLength; i++) { + typeswitch (this.fixedArray[i]) { + case (n: Number): { + elements[i] = Convert(n); + } + case (h: HeapObject): { + assert(h == Hole); + } + } + } + a.elements = elements; + } else { + // Simply install the given fixedArray in {vector}. + a = new JSArray{map, this.fixedArray}; + } + + // Paranoia. the FixedArray now "belongs" to JSArray {a}. + this.fixedArray = kEmptyFixedArray; + return a; + } + + StoreResult(implicit context: Context)(index: Smi, result: Object) { + typeswitch (result) { + case (s: Smi): { + this.fixedArray[index] = s; + } + case (s: HeapNumber): { + this.onlySmis = false; + this.fixedArray[index] = s; + } + case (s: HeapObject): { + this.onlySmis = false; + this.onlyNumbers = false; + this.fixedArray[index] = s; + } + } + } + + fixedArray: FixedArray; + onlySmis: bool; // initially true. + onlyNumbers: bool; // initially true. + skippedElements: bool; // initially false. + } + + transitioning macro + MapVisitAllElements(implicit context: Context)( + o: JSArray, len: Smi, callbackfn: Callable, thisArg: Object, + vector: Vector): Vector labels Bailout(Vector, Smi) { + let k: Smi = 0; + let v: Vector = vector; + const fastOWitness: FastJSArrayWitness = + MakeWitness(Cast(o) otherwise goto Bailout(v, k)); + + // Build a fast loop over the smi array. + // 7. Repeat, while k < len. + for (; k < len; k = k + 1) { + let fastO: FastJSArray = + Testify(fastOWitness) otherwise goto Bailout(v, k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastO.length) goto Bailout(v, k); + + try { + const value: Object = + LoadElementNoHole(fastO, k) otherwise FoundHole; + const result: Object = + Call(context, callbackfn, thisArg, value, k, fastO); + v.StoreResult(k, result); + } + label FoundHole { + // Our output array must necessarily be holey because of holes in + // the input array. + v.ReportSkippedElement(); + } + } + + return v; + } + + transitioning macro FastArrayMap(implicit context: Context)( + o: JSReceiver, len: Smi, callbackfn: Callable, thisArg: Object): JSArray + labels Bailout(JSArray, Smi) { + let k: Smi = 0; + let fastO: FastJSArray = Cast(o) otherwise unreachable; + let vector: Vector = Vector{len}; + const elementsKind: ElementsKind = fastO.map.elements_kind; + try { + if (IsElementsKindLessThanOrEqual(elementsKind, HOLEY_SMI_ELEMENTS)) { + vector = MapVisitAllElements( + fastO, len, callbackfn, thisArg, vector) + otherwise InnerBailout; + } else if (IsElementsKindLessThanOrEqual(elementsKind, HOLEY_ELEMENTS)) { + vector = MapVisitAllElements( + fastO, len, callbackfn, thisArg, vector) + otherwise InnerBailout; + } else { + assert(IsDoubleElementsKind(elementsKind)); + vector = MapVisitAllElements( + fastO, len, callbackfn, thisArg, vector) + otherwise InnerBailout; + } + } + label InnerBailout(v: Vector, k: Smi) { + // Transform the Vector {v} into a JSArray and bail out. + let vector: Vector = v; + goto Bailout(vector.CreateJSArray(k), k); + } + + return vector.CreateJSArray(len); + } + + // Bails out if the slow path needs to be taken. + // It's useful to structure it this way, because the consequences of + // using the slow path on species creation are interesting to the caller. + macro FastMapSpeciesCreate(implicit context: Context)( + receiver: JSReceiver, length: Number): JSArray labels Bailout { + if (IsArraySpeciesProtectorCellInvalid()) goto Bailout; + const o: FastJSArray = Cast(receiver) otherwise Bailout; + const smiLength: Smi = Cast(length) otherwise Bailout; + const newMap: Map = + LoadJSArrayElementsMap(PACKED_SMI_ELEMENTS, LoadNativeContext(context)); + return AllocateJSArray(PACKED_SMI_ELEMENTS, newMap, smiLength, smiLength); + } + + // https://tc39.github.io/ecma262/#sec-array.prototype.map + transitioning javascript builtin + ArrayMap(implicit context: Context)(receiver: Object, ...arguments): Object { + try { + if (IsNullOrUndefined(receiver)) goto NullOrUndefinedError; + + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); + + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) goto TypeError; + + const callbackfn: Callable = + Cast(arguments[0]) otherwise TypeError; + + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined; + + let array: JSReceiver; + let k: Number = 0; + try { + // 5. Let A be ? ArraySpeciesCreate(O, len). + if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate; + const o: FastJSArray = Cast(receiver) + otherwise SlowSpeciesCreate; + const smiLength: Smi = Cast(len) + otherwise SlowSpeciesCreate; + + return FastArrayMap(o, smiLength, callbackfn, thisArg) + otherwise Bailout; + } + label SlowSpeciesCreate { + array = ArraySpeciesCreate(context, receiver, len); + } + label Bailout(output: JSArray, kValue: Smi) deferred { + array = output; + k = kValue; + } + + return ArrayMapLoopContinuation( + o, callbackfn, thisArg, array, o, k, len, Undefined); + } + label TypeError deferred { + ThrowTypeError(context, kCalledNonCallable, arguments[0]); + } + label NullOrUndefinedError deferred { + ThrowTypeError(context, kCalledOnNullOrUndefined, 'Array.prototype.map'); + } + } +} diff --git a/src/builtins/base.tq b/src/builtins/base.tq index 7887fa1383..159e14a51c 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -93,6 +93,10 @@ class JSArray extends JSObject { kEmptyFixedArray); this.length = 0; } + constructor(implicit context: Context)(map: Map, elements: FixedArrayBase) { + super(map, kEmptyFixedArray, elements); + this.length = elements.length; + } IsEmpty(): bool { return this.length == 0; } @@ -232,11 +236,12 @@ const BIGINT64_ELEMENTS: const kNone: constexpr AllocationFlags generates 'CodeStubAssembler::kNone'; -const kDoubleAlignment: - constexpr AllocationFlags generates 'kDoubleAlignment'; -const kPretenured: constexpr AllocationFlags generates 'kPretenured'; -const kAllowLargeObjectAllocation: - constexpr AllocationFlags generates 'kAllowLargeObjectAllocation'; +const kDoubleAlignment: constexpr AllocationFlags + generates 'CodeStubAssembler::kDoubleAlignment'; +const kPretenured: + constexpr AllocationFlags generates 'CodeStubAssembler::kPretenured'; +const kAllowLargeObjectAllocation: constexpr AllocationFlags + generates 'CodeStubAssembler::kAllowLargeObjectAllocation'; type FixedUint8Array extends FixedTypedArray; type FixedInt8Array extends FixedTypedArray; @@ -1072,8 +1077,8 @@ UnsafeCast(o: Object): Object { } const kCOWMap: Map = %RawObjectCast(LoadRoot(kFixedCOWArrayMapRootIndex)); -const kEmptyFixedArray: FixedArrayBase = - %RawObjectCast(LoadRoot(kEmptyFixedArrayRootIndex)); +const kEmptyFixedArray: FixedArray = + %RawObjectCast(LoadRoot(kEmptyFixedArrayRootIndex)); extern macro IsPrototypeInitialArrayPrototype(implicit context: Context)(Map): bool; @@ -1105,6 +1110,7 @@ extern operator '[]' macro LoadFixedArrayElement( FixedArray, constexpr int31): Object; extern operator '[]=' macro StoreFixedArrayElement( FixedArray, intptr, Smi): void; +extern operator '[]=' macro StoreFixedArrayElement(FixedArray, Smi, Smi): void; extern operator '[]=' macro StoreFixedArrayElement( FixedArray, intptr, HeapObject): void; extern operator '[]=' macro StoreFixedArrayElement( @@ -1148,6 +1154,16 @@ extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool; extern macro IsFastSmiElementsKind(ElementsKind): bool; extern macro IsHoleyFastElementsKind(ElementsKind): bool; +macro FastHoleyElementsKind(kind: ElementsKind): ElementsKind { + if (kind == PACKED_SMI_ELEMENTS) { + return HOLEY_SMI_ELEMENTS; + } else if (kind == PACKED_DOUBLE_ELEMENTS) { + return HOLEY_DOUBLE_ELEMENTS; + } + assert(kind == PACKED_ELEMENTS); + return HOLEY_ELEMENTS; +} + macro AllowDoubleElements(kind: ElementsKind): ElementsKind { if (kind == PACKED_SMI_ELEMENTS) { return PACKED_DOUBLE_ELEMENTS; @@ -1177,6 +1193,8 @@ extern macro CalculateNewElementsCapacity(intptr): intptr; extern macro AllocateFixedArrayWithHoles( intptr, constexpr AllocationFlags): FixedArray; +extern macro AllocateFixedDoubleArrayWithHoles( + intptr, constexpr AllocationFlags): FixedDoubleArray; extern macro CopyFixedArrayElements( constexpr ElementsKind, FixedArray, constexpr ElementsKind, FixedArray, intptr, intptr, intptr): void; @@ -1186,7 +1204,6 @@ extern macro CopyFixedArrayElements( extern macro AllocateJSArray(constexpr ElementsKind, Map, intptr, Smi): JSArray; extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray; - extern macro AllocateJSObjectFromMap(Map): JSObject; extern operator '[]=' macro StoreFixedDoubleArrayElementSmi( @@ -1300,7 +1317,8 @@ LoadElementNoHole(implicit context: Context)( } extern macro TransitionElementsKind( - JSObject, Map, ElementsKind, ElementsKind): void labels Bailout; + JSObject, Map, constexpr ElementsKind, + constexpr ElementsKind): void labels Bailout; extern macro IsCallable(HeapObject): bool; extern macro IsJSArray(HeapObject): bool; diff --git a/src/builtins/builtins-array-gen.cc b/src/builtins/builtins-array-gen.cc index db58ecf152..1cfc5bf1eb 100644 --- a/src/builtins/builtins-array-gen.cc +++ b/src/builtins/builtins-array-gen.cc @@ -126,10 +126,6 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { BIND(&ok); } - void ArrayBuiltinsAssembler::MapResultGenerator() { - GenerateArraySpeciesCreate(len_); - } - void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() { // 6. Let A be ? TypedArraySpeciesCreate(O, len). TNode original_array = CAST(o()); @@ -148,120 +144,6 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { a_.Bind(a); } - Node* ArrayBuiltinsAssembler::SpecCompliantMapProcessor(Node* k_value, - Node* k) { - // i. Let kValue be ? Get(O, Pk). Performed by the caller of - // SpecCompliantMapProcessor. - // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). - Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(), - callbackfn(), this_arg(), k_value, k, o()); - - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). - CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, mapped_value); - return a(); - } - - Node* ArrayBuiltinsAssembler::FastMapProcessor(Node* k_value, Node* k) { - // i. Let kValue be ? Get(O, Pk). Performed by the caller of - // FastMapProcessor. - // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). - Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(), - callbackfn(), this_arg(), k_value, k, o()); - - // mode is SMI_PARAMETERS because k has tagged representation. - ParameterMode mode = SMI_PARAMETERS; - Label runtime(this), finished(this); - Label transition_pre(this), transition_smi_fast(this), - transition_smi_double(this); - Label array_not_smi(this), array_fast(this), array_double(this); - - TNode kind = LoadElementsKind(a()); - Node* elements = LoadElements(a()); - GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), &array_not_smi); - TryStoreArrayElement(HOLEY_SMI_ELEMENTS, mode, &transition_pre, elements, k, - mapped_value); - Goto(&finished); - - BIND(&transition_pre); - { - // array is smi. Value is either tagged or a heap number. - CSA_ASSERT(this, TaggedIsNotSmi(mapped_value)); - GotoIf(IsHeapNumberMap(LoadMap(mapped_value)), &transition_smi_double); - Goto(&transition_smi_fast); - } - - BIND(&array_not_smi); - { - Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &array_double, - &array_fast); - } - - BIND(&transition_smi_fast); - { - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). - Node* const native_context = LoadNativeContext(context()); - Node* const fast_map = LoadContextElement( - native_context, Context::JS_ARRAY_HOLEY_ELEMENTS_MAP_INDEX); - - // Since this transition is only a map change, just do it right here. - // Since a() doesn't have an allocation site, it's safe to do the - // map store directly, otherwise I'd call TransitionElementsKind(). - StoreMap(a(), fast_map); - Goto(&array_fast); - } - - BIND(&array_fast); - { - TryStoreArrayElement(HOLEY_ELEMENTS, mode, &runtime, elements, k, - mapped_value); - Goto(&finished); - } - - BIND(&transition_smi_double); - { - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). - Node* const native_context = LoadNativeContext(context()); - Node* const double_map = LoadContextElement( - native_context, Context::JS_ARRAY_HOLEY_DOUBLE_ELEMENTS_MAP_INDEX); - - const ElementsKind kFromKind = HOLEY_SMI_ELEMENTS; - const ElementsKind kToKind = HOLEY_DOUBLE_ELEMENTS; - - Label transition_in_runtime(this, Label::kDeferred); - TransitionElementsKind(a(), double_map, kFromKind, kToKind, - &transition_in_runtime); - Goto(&array_double); - - BIND(&transition_in_runtime); - CallRuntime(Runtime::kTransitionElementsKind, context(), a(), double_map); - Goto(&array_double); - } - - BIND(&array_double); - { - // TODO(mvstanton): If we use a variable for elements and bind it - // appropriately, we can avoid an extra load of elements by binding the - // value only after a transition from smi to double. - elements = LoadElements(a()); - // If the mapped_value isn't a number, this will bail out to the runtime - // to make the transition. - TryStoreArrayElement(HOLEY_DOUBLE_ELEMENTS, mode, &runtime, elements, k, - mapped_value); - Goto(&finished); - } - - BIND(&runtime); - { - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). - CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, - mapped_value); - Goto(&finished); - } - - BIND(&finished); - return a(); - } - // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map. Node* ArrayBuiltinsAssembler::TypedArrayMapProcessor(Node* k_value, Node* k) { // 8. c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »). @@ -2128,84 +2010,6 @@ TF_BUILTIN(TypedArrayPrototypeReduceRight, ArrayBuiltinsAssembler) { ForEachDirection::kReverse); } -TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinsAssembler) { - TNode context = CAST(Parameter(Descriptor::kContext)); - TNode receiver = CAST(Parameter(Descriptor::kReceiver)); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* array = Parameter(Descriptor::kArray); - TNode object = CAST(Parameter(Descriptor::kObject)); - Node* initial_k = Parameter(Descriptor::kInitialK); - TNode len = CAST(Parameter(Descriptor::kLength)); - Node* to = Parameter(Descriptor::kTo); - - InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, - this_arg, array, object, initial_k, - len, to); - - GenerateIteratingArrayBuiltinLoopContinuation( - &ArrayBuiltinsAssembler::SpecCompliantMapProcessor, - &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip); -} - -TF_BUILTIN(ArrayMapLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { - TNode context = CAST(Parameter(Descriptor::kContext)); - TNode receiver = CAST(Parameter(Descriptor::kReceiver)); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* array = Parameter(Descriptor::kArray); - Node* initial_k = Parameter(Descriptor::kInitialK); - TNode len = CAST(Parameter(Descriptor::kLength)); - - Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver, - callbackfn, this_arg, array, receiver, initial_k, len, - UndefinedConstant())); -} - -TF_BUILTIN(ArrayMapLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { - TNode context = CAST(Parameter(Descriptor::kContext)); - TNode receiver = CAST(Parameter(Descriptor::kReceiver)); - Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* this_arg = Parameter(Descriptor::kThisArg); - Node* array = Parameter(Descriptor::kArray); - Node* initial_k = Parameter(Descriptor::kInitialK); - TNode len = CAST(Parameter(Descriptor::kLength)); - Node* result = Parameter(Descriptor::kResult); - - // This custom lazy deopt point is right after the callback. map() needs - // to pick up at the next step, which is setting the callback result in - // the output array. After incrementing k, we can glide into the loop - // continuation builtin. - - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). - CallRuntime(Runtime::kCreateDataProperty, context, array, initial_k, result); - // Then we have to increment k before going on. - initial_k = NumberInc(initial_k); - - Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver, - callbackfn, this_arg, array, receiver, initial_k, len, - UndefinedConstant())); -} - -TF_BUILTIN(ArrayMap, ArrayBuiltinsAssembler) { - TNode argc = - ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); - CodeStubArguments args(this, argc); - TNode context = CAST(Parameter(Descriptor::kContext)); - TNode receiver = args.GetReceiver(); - Node* callbackfn = args.GetOptionalArgumentValue(0); - Node* this_arg = args.GetOptionalArgumentValue(1); - - InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc); - - GenerateIteratingArrayBuiltinBody( - "Array.prototype.map", &ArrayBuiltinsAssembler::MapResultGenerator, - &ArrayBuiltinsAssembler::FastMapProcessor, - &ArrayBuiltinsAssembler::NullPostLoopAction, - Builtins::CallableFor(isolate(), Builtins::kArrayMapLoopContinuation), - MissingPropertyMode::kSkip); -} - TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinsAssembler) { TNode argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index e0abf90f0d..5f429cf19c 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -364,14 +364,6 @@ namespace internal { TFJ(ArraySomeLoopLazyDeoptContinuation, 5, kReceiver, kCallbackFn, kThisArg, \ kInitialK, kLength, kResult) \ TFJ(ArraySome, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ - /* ES6 #sec-array.prototype.foreach */ \ - TFS(ArrayMapLoopContinuation, kReceiver, kCallbackFn, kThisArg, kArray, \ - kObject, kInitialK, kLength, kTo) \ - TFJ(ArrayMapLoopEagerDeoptContinuation, 5, kReceiver, kCallbackFn, kThisArg, \ - kArray, kInitialK, kLength) \ - TFJ(ArrayMapLoopLazyDeoptContinuation, 6, kReceiver, kCallbackFn, kThisArg, \ - kArray, kInitialK, kLength, kResult) \ - TFJ(ArrayMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ /* ES6 #sec-array.prototype.reduce */ \ TFS(ArrayReduceLoopContinuation, kReceiver, kCallbackFn, kThisArg, \ kAccumulator, kObject, kInitialK, kLength, kTo) \ diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 4dfd176eaa..7dfcb0fb6d 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -1262,6 +1262,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode value) { StoreFixedArrayElement(array, index, value, SKIP_WRITE_BARRIER, 0); } + void StoreFixedArrayElement(TNode array, TNode index, + TNode value) { + StoreFixedArrayElement(array, index, value, SKIP_WRITE_BARRIER, 0, + SMI_PARAMETERS); + } void StoreFixedDoubleArrayElement( TNode object, Node* index, TNode value, @@ -1532,6 +1537,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler return result; } + TNode AllocateFixedDoubleArrayWithHoles( + TNode capacity, AllocationFlags flags) { + TNode result = UncheckedCast( + AllocateFixedArray(PACKED_DOUBLE_ELEMENTS, capacity, flags)); + FillFixedArrayWithValue(PACKED_DOUBLE_ELEMENTS, result, IntPtrConstant(0), + capacity, RootIndex::kTheHoleValue); + return result; + } + Node* AllocatePropertyArray(Node* capacity, ParameterMode mode = INTPTR_PARAMETERS, AllocationFlags flags = kNone);