[Torque] Array.prototype.map implemented in Torque
Change-Id: I3a60be25b9c7daadcad6078447348b790b249e1c Reviewed-on: https://chromium-review.googlesource.com/c/1402774 Commit-Queue: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#59042}
This commit is contained in:
parent
7e616f2b6e
commit
304e74c8b3
1
BUILD.gn
1
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",
|
||||
|
318
src/builtins/array-map.tq
Normal file
318
src/builtins/array-map.tq
Normal file
@ -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<JSReceiver>(receiver) otherwise unreachable;
|
||||
const callbackfn: Callable = Cast<Callable>(callback) otherwise unreachable;
|
||||
const outputArray: JSReceiver =
|
||||
Cast<JSReceiver>(array) otherwise unreachable;
|
||||
const numberK: Number = Cast<Number>(initialK) otherwise unreachable;
|
||||
const numberLength: Number = Cast<Number>(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<JSReceiver>(receiver) otherwise unreachable;
|
||||
const callbackfn: Callable = Cast<Callable>(callback) otherwise unreachable;
|
||||
const outputArray: JSReceiver =
|
||||
Cast<JSReceiver>(array) otherwise unreachable;
|
||||
let numberK: Number = Cast<Number>(initialK) otherwise unreachable;
|
||||
const numberLength: Number = Cast<Number>(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<float64>(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<FixedArrayType: type>(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<FastJSArray>(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<FixedArrayType>(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<FastJSArray>(o) otherwise unreachable;
|
||||
let vector: Vector = Vector{len};
|
||||
const elementsKind: ElementsKind = fastO.map.elements_kind;
|
||||
try {
|
||||
if (IsElementsKindLessThanOrEqual(elementsKind, HOLEY_SMI_ELEMENTS)) {
|
||||
vector = MapVisitAllElements<FixedArray>(
|
||||
fastO, len, callbackfn, thisArg, vector)
|
||||
otherwise InnerBailout;
|
||||
} else if (IsElementsKindLessThanOrEqual(elementsKind, HOLEY_ELEMENTS)) {
|
||||
vector = MapVisitAllElements<FixedArray>(
|
||||
fastO, len, callbackfn, thisArg, vector)
|
||||
otherwise InnerBailout;
|
||||
} else {
|
||||
assert(IsDoubleElementsKind(elementsKind));
|
||||
vector = MapVisitAllElements<FixedDoubleArray>(
|
||||
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<FastJSArray>(receiver) otherwise Bailout;
|
||||
const smiLength: Smi = Cast<Smi>(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<Callable>(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<FastJSArray>(receiver)
|
||||
otherwise SlowSpeciesCreate;
|
||||
const smiLength: Smi = Cast<Smi>(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');
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Object>(o: Object): Object {
|
||||
}
|
||||
|
||||
const kCOWMap: Map = %RawObjectCast<Map>(LoadRoot(kFixedCOWArrayMapRootIndex));
|
||||
const kEmptyFixedArray: FixedArrayBase =
|
||||
%RawObjectCast<FixedArrayBase>(LoadRoot(kEmptyFixedArrayRootIndex));
|
||||
const kEmptyFixedArray: FixedArray =
|
||||
%RawObjectCast<FixedArray>(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<FixedDoubleArray>(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;
|
||||
|
@ -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<JSTypedArray> 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<Int32T> 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> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
Node* callbackfn = Parameter(Descriptor::kCallbackFn);
|
||||
Node* this_arg = Parameter(Descriptor::kThisArg);
|
||||
Node* array = Parameter(Descriptor::kArray);
|
||||
TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
|
||||
Node* initial_k = Parameter(Descriptor::kInitialK);
|
||||
TNode<Number> 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> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> 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<Number> 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> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> 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<Number> 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<IntPtrT> argc =
|
||||
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
|
||||
CodeStubArguments args(this, argc);
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> 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<IntPtrT> argc =
|
||||
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
|
||||
|
@ -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) \
|
||||
|
@ -1262,6 +1262,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<Smi> value) {
|
||||
StoreFixedArrayElement(array, index, value, SKIP_WRITE_BARRIER, 0);
|
||||
}
|
||||
void StoreFixedArrayElement(TNode<FixedArray> array, TNode<Smi> index,
|
||||
TNode<Smi> value) {
|
||||
StoreFixedArrayElement(array, index, value, SKIP_WRITE_BARRIER, 0,
|
||||
SMI_PARAMETERS);
|
||||
}
|
||||
|
||||
void StoreFixedDoubleArrayElement(
|
||||
TNode<FixedDoubleArray> object, Node* index, TNode<Float64T> value,
|
||||
@ -1532,6 +1537,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
return result;
|
||||
}
|
||||
|
||||
TNode<FixedDoubleArray> AllocateFixedDoubleArrayWithHoles(
|
||||
TNode<IntPtrT> capacity, AllocationFlags flags) {
|
||||
TNode<FixedDoubleArray> result = UncheckedCast<FixedDoubleArray>(
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user