[builtins] Implement Array.prototype.slice in Torque
In the process: - add volatile types for FastJSArray and remove the length_fast accessor from JSArray with the application of more rigorous typing. - add micro benchmarks for testing all the interesting slice cases Also update a few assorted places in .tq code to make them more idiomatic. Change-Id: I76ec2bb25b65a869180af1f7288419dc1f0a9c37 Reviewed-on: https://chromium-review.googlesource.com/c/1281603 Commit-Queue: Daniel Clifford <danno@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#56806}
This commit is contained in:
parent
2d8adf3db2
commit
41ba3d3eb0
1
BUILD.gn
1
BUILD.gn
@ -917,6 +917,7 @@ torque_files = [
|
||||
"src/builtins/array-join.tq",
|
||||
"src/builtins/array-lastindexof.tq",
|
||||
"src/builtins/array-reverse.tq",
|
||||
"src/builtins/array-slice.tq",
|
||||
"src/builtins/array-splice.tq",
|
||||
"src/builtins/array-unshift.tq",
|
||||
"src/builtins/typed-array.tq",
|
||||
|
83
benchmarks/micro/slice-perf.js
Normal file
83
benchmarks/micro/slice-perf.js
Normal file
@ -0,0 +1,83 @@
|
||||
// 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.
|
||||
|
||||
const kIterations = 1000000;
|
||||
const kIterationShort = 10000;
|
||||
const kArraySize = 64;
|
||||
|
||||
let smi_array = [];
|
||||
for (let i = 0; i < kArraySize; ++i) smi_array[i] = Math.floor(Math.random() * 100);
|
||||
|
||||
let start = performance.now();
|
||||
for (let x = 0; x < kIterations; ++x) {
|
||||
smi_array.slice(0);
|
||||
}
|
||||
let stop = performance.now();
|
||||
print("smi_array copy: " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterations; ++x) {
|
||||
smi_array.slice(x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("smi_array: " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
let double_array = [];
|
||||
for (let i = 0; i < kArraySize; ++i) double_array[i] = Math.random() * 100;
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterations; ++x) {
|
||||
double_array.slice(x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("double_array: " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
let object_array = [];
|
||||
for (let i = 0; i < kArraySize; ++i) object_array[i] = new Object();
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterations; ++x) {
|
||||
object_array.slice(x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("object_array: " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
let dictionary_array = [];
|
||||
for (let i = 0; i < kArraySize; ++i) dictionary_array[i] = new Object();
|
||||
dictionary_array[100000] = new Object();
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterationShort; ++x) {
|
||||
dictionary_array.slice(x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("dictionary: " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
let arguments_array;
|
||||
function sloppy() {
|
||||
arguments_array = arguments;
|
||||
}
|
||||
sloppy.apply(null, smi_array);
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterations; ++x) {
|
||||
let r = Array.prototype.slice.call(arguments_array, x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("arguments_array (sloppy): " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
function sloppy2 (a) {
|
||||
arguments_array = arguments;
|
||||
}
|
||||
sloppy2.apply(null, smi_array);
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterations; ++x) {
|
||||
Array.prototype.slice.call(arguments_array, x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("arguments_array (fast aliased): " + (Math.floor((stop - start)*10)/10) + " ms");
|
||||
|
||||
delete arguments_array[5];
|
||||
start = performance.now();
|
||||
for (let x = 0; x < kIterationShort; ++x) {
|
||||
Array.prototype.slice.call(arguments_array, x % kArraySize);
|
||||
}
|
||||
stop = performance.now();
|
||||
print("arguments_array (slow aliased): " + (Math.floor((stop - start)*10)/10) + " ms");
|
@ -1739,8 +1739,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
Builtins::kArrayPrototypeShift, 0, false);
|
||||
SimpleInstallFunction(isolate_, proto, "unshift",
|
||||
Builtins::kArrayPrototypeUnshift, 1, false);
|
||||
SimpleInstallFunction(isolate_, proto, "slice",
|
||||
Builtins::kArrayPrototypeSlice, 2, false);
|
||||
SimpleInstallFunction(isolate_, proto, "slice", Builtins::kArraySlice, 2,
|
||||
false);
|
||||
SimpleInstallFunction(isolate_, proto, "sort",
|
||||
Builtins::kArrayPrototypeSort, 1, false);
|
||||
SimpleInstallFunction(isolate_, proto, "splice", Builtins::kArraySplice, 2,
|
||||
|
@ -75,10 +75,8 @@ module array {
|
||||
context: Context, receiver: JSReceiver, searchElement: Object,
|
||||
from: Number): Object
|
||||
labels Slow {
|
||||
EnsureFastJSArray(context, receiver) otherwise Slow;
|
||||
const array: JSArray = UnsafeCast<JSArray>(receiver);
|
||||
|
||||
const length: Smi = array.length_fast;
|
||||
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||
const length: Smi = array.length;
|
||||
if (length == 0) return SmiConstant(-1);
|
||||
|
||||
const fromSmi: Smi = Cast<Smi>(from) otherwise Slow;
|
||||
|
@ -143,21 +143,22 @@ module array {
|
||||
return object;
|
||||
}
|
||||
|
||||
macro TryFastPackedArrayReverse(receiver: Object) labels Slow {
|
||||
const array: JSArray = Cast<JSArray>(receiver) otherwise Slow;
|
||||
macro TryFastPackedArrayReverse(implicit context: Context)(receiver: Object)
|
||||
labels Slow {
|
||||
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||
|
||||
const kind: ElementsKind = array.map.elements_kind;
|
||||
if (kind == PACKED_SMI_ELEMENTS) {
|
||||
EnsureWriteableFastElements(array);
|
||||
FastPackedArrayReverse<FastPackedSmiElements, Smi>(
|
||||
array.elements, array.length_fast);
|
||||
array.elements, array.length);
|
||||
} else if (kind == PACKED_ELEMENTS) {
|
||||
EnsureWriteableFastElements(array);
|
||||
FastPackedArrayReverse<FastPackedObjectElements, Object>(
|
||||
array.elements, array.length_fast);
|
||||
array.elements, array.length);
|
||||
} else if (kind == PACKED_DOUBLE_ELEMENTS) {
|
||||
FastPackedArrayReverse<FastPackedDoubleElements, float64>(
|
||||
array.elements, array.length_fast);
|
||||
array.elements, array.length);
|
||||
} else {
|
||||
goto Slow;
|
||||
}
|
||||
|
208
src/builtins/array-slice.tq
Normal file
208
src/builtins/array-slice.tq
Normal file
@ -0,0 +1,208 @@
|
||||
// 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.
|
||||
|
||||
module array {
|
||||
macro HandleSimpleArgumentsSlice(
|
||||
context: Context, args: JSArgumentsObjectWithLength, start: Smi,
|
||||
count: Smi): JSArray
|
||||
labels Bailout {
|
||||
const end: Smi = start + count;
|
||||
const sourceElements: FixedArray =
|
||||
Cast<FixedArray>(args.elements) otherwise Bailout;
|
||||
if (SmiAbove(end, sourceElements.length)) goto Bailout;
|
||||
|
||||
const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context);
|
||||
const result: JSArray =
|
||||
AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count);
|
||||
const newElements: FixedArray =
|
||||
Cast<FixedArray>(result.elements) otherwise Bailout;
|
||||
CopyElements(
|
||||
PACKED_ELEMENTS, newElements, Convert<intptr>(0), sourceElements,
|
||||
Convert<intptr>(start), Convert<intptr>(count));
|
||||
return result;
|
||||
}
|
||||
|
||||
macro HandleFastAliasedSloppyArgumentsSlice(
|
||||
context: Context, args: JSArgumentsObjectWithLength, start: Smi,
|
||||
count: Smi): JSArray
|
||||
labels Bailout {
|
||||
const sloppyElements: SloppyArgumentsElements =
|
||||
Cast<SloppyArgumentsElements>(args.elements) otherwise Bailout;
|
||||
const sloppyElementsLength: Smi = sloppyElements.length;
|
||||
const parameterMapLength: Smi =
|
||||
sloppyElementsLength - kSloppyArgumentsParameterMapStart;
|
||||
|
||||
// Check to make sure that the extraction will not access outside the
|
||||
// defined arguments
|
||||
const end: Smi = start + count;
|
||||
const unmappedElements: FixedArray =
|
||||
Cast<FixedArray>(sloppyElements[kSloppyArgumentsArgumentsIndex])
|
||||
otherwise Bailout;
|
||||
const unmappedElementsLength: Smi = unmappedElements.length;
|
||||
if (SmiAbove(end, unmappedElementsLength)) goto Bailout;
|
||||
|
||||
const argumentsContext: Context =
|
||||
UnsafeCast<Context>(sloppyElements[kSloppyArgumentsContextIndex]);
|
||||
|
||||
const arrayMap: Map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, context);
|
||||
const result: JSArray =
|
||||
AllocateJSArray(HOLEY_ELEMENTS, arrayMap, count, count);
|
||||
|
||||
let indexOut: Smi = 0;
|
||||
const resultElements: FixedArray = UnsafeCast<FixedArray>(result.elements);
|
||||
const to: Smi = SmiMin(parameterMapLength, end);
|
||||
|
||||
// Fill in the part of the result that map to context-mapped parameters.
|
||||
for (let current: Smi = start; current < to; ++current) {
|
||||
const e: Object =
|
||||
sloppyElements[current + kSloppyArgumentsParameterMapStart];
|
||||
const newElement: Object = e != Hole ?
|
||||
argumentsContext[UnsafeCast<Smi>(e)] :
|
||||
unmappedElements[current];
|
||||
StoreFixedArrayElementSmi(
|
||||
resultElements, indexOut++, newElement, SKIP_WRITE_BARRIER);
|
||||
}
|
||||
|
||||
// Fill in the rest of the result that contains the unmapped parameters
|
||||
// above the formal parameters.
|
||||
const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end);
|
||||
const restCount: Smi = end - unmappedFrom;
|
||||
CopyElements(
|
||||
PACKED_ELEMENTS, resultElements, Convert<intptr>(indexOut),
|
||||
unmappedElements, Convert<intptr>(unmappedFrom),
|
||||
Convert<intptr>(restCount));
|
||||
return result;
|
||||
}
|
||||
|
||||
macro HandleFastSlice(
|
||||
context: Context, o: Object, startNumber: Number, countNumber: Number):
|
||||
JSArray
|
||||
labels Bailout {
|
||||
const start: Smi = Cast<Smi>(startNumber) otherwise Bailout;
|
||||
const count: Smi = Cast<Smi>(countNumber) otherwise Bailout;
|
||||
assert(start >= 0);
|
||||
|
||||
// If the resulting array doesn't fit in new space, use the slow path.
|
||||
if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout;
|
||||
|
||||
typeswitch (o) {
|
||||
case (a: FastJSArrayForCopy): {
|
||||
// It's possible to modify the array length from a valueOf
|
||||
// callback between the original array length read and this
|
||||
// point. That can change the length of the array backing store,
|
||||
// in the worst case, making it smaller than the region that needs
|
||||
// to be copied out. Therefore, re-check the length before calling
|
||||
// the appropriate fast path. See regress-785804.js
|
||||
if (SmiAbove(start + count, a.length)) goto Bailout;
|
||||
return ExtractFastJSArray(context, a, start, count);
|
||||
}
|
||||
case (a: JSArgumentsObjectWithLength): {
|
||||
const nativeContext: NativeContext = LoadNativeContext(context);
|
||||
const map: Map = a.map;
|
||||
if (IsFastAliasedArgumentsMap(map)) {
|
||||
return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count)
|
||||
otherwise Bailout;
|
||||
} else if (IsStrictArgumentsMap(map) || IsSloppyArgumentsMap(map)) {
|
||||
return HandleSimpleArgumentsSlice(context, a, start, count)
|
||||
otherwise Bailout;
|
||||
}
|
||||
}
|
||||
case (Object): {
|
||||
}
|
||||
}
|
||||
goto Bailout;
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.slice
|
||||
javascript builtin ArraySlice(
|
||||
context: Context, receiver: Object, ...arguments): Object {
|
||||
// Handle array cloning case if the receiver is a fast array.
|
||||
if (arguments.length == 0) {
|
||||
typeswitch (receiver) {
|
||||
case (a: FastJSArrayForCopy): {
|
||||
return CloneFastJSArray(context, a);
|
||||
}
|
||||
case (Object): {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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. Let relativeStart be ? ToInteger(start).
|
||||
const start: Object = arguments[0];
|
||||
const relativeStart: Number = ToInteger_Inline(context, start);
|
||||
|
||||
// 4. If relativeStart < 0, let k be max((len + relativeStart), 0);
|
||||
// else let k be min(relativeStart, len).
|
||||
let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) :
|
||||
Min(relativeStart, len);
|
||||
|
||||
// 5. If end is undefined, let relativeEnd be len;
|
||||
// else let relativeEnd be ? ToInteger(end).
|
||||
const end: Object = arguments[1];
|
||||
const relativeEnd: Number =
|
||||
end == Undefined ? len : ToInteger_Inline(context, end);
|
||||
|
||||
// 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
|
||||
// else let final be min(relativeEnd, len).
|
||||
const final: Number =
|
||||
relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len);
|
||||
|
||||
// 7. Let count be max(final - k, 0).
|
||||
const count: Number = Max(final - k, 0);
|
||||
|
||||
assert(0 <= k);
|
||||
assert(k <= len);
|
||||
assert(0 <= final);
|
||||
assert(final <= len);
|
||||
assert(0 <= count);
|
||||
assert(count <= len);
|
||||
|
||||
try {
|
||||
return HandleFastSlice(context, o, k, count) otherwise Slow;
|
||||
}
|
||||
label Slow {}
|
||||
|
||||
// 8. Let A be ? ArraySpeciesCreate(O, count).
|
||||
const a: JSReceiver = ArraySpeciesCreate(context, o, count);
|
||||
|
||||
// 9. Let n be 0.
|
||||
let n: Number = 0;
|
||||
|
||||
// 10. Repeat, while k < final
|
||||
while (k < final) {
|
||||
// a. Let Pk be ! ToString(k).
|
||||
let pK: Number = k;
|
||||
|
||||
// b. Let kPresent be ? HasProperty(O, Pk).
|
||||
const fromPresent: Boolean = HasProperty(o, pK);
|
||||
|
||||
// c. If kPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// i. Let kValue be ? Get(O, Pk).
|
||||
const kValue: Object = GetProperty(o, pK);
|
||||
|
||||
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue).
|
||||
CreateDataProperty(a, n, kValue);
|
||||
}
|
||||
|
||||
// d. Increase k by 1.
|
||||
k++;
|
||||
|
||||
// e. Increase n by 1.
|
||||
n++;
|
||||
}
|
||||
|
||||
// 11. Perform ? Set(A, "length", n, true).
|
||||
SetProperty(a, kLengthString, n);
|
||||
|
||||
// 12. Return A.
|
||||
return a;
|
||||
}
|
||||
}
|
@ -9,8 +9,7 @@ module array {
|
||||
context: Context, receiver: Object, arguments: constexpr Arguments):
|
||||
never
|
||||
labels Slow {
|
||||
EnsureFastJSArray(context, receiver) otherwise Slow;
|
||||
const array: JSArray = UnsafeCast<JSArray>(receiver);
|
||||
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||
EnsureWriteableFastElements(array);
|
||||
|
||||
const map: Map = array.map;
|
||||
|
@ -24,7 +24,7 @@ module array {
|
||||
// extract FixedArrays and don't have to worry about FixedDoubleArrays.
|
||||
assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind));
|
||||
|
||||
const length: Smi = array.length_fast;
|
||||
const length: Smi = Cast<Smi>(array.length) otherwise unreachable;
|
||||
array.elements =
|
||||
ExtractFixedArray(elements, 0, length, length, kFixedArrays);
|
||||
assert(array.elements.map != kCOWMap);
|
||||
|
@ -40,6 +40,10 @@ type JSArgumentsObjectWithLength extends JSObject
|
||||
generates 'TNode<JSArgumentsObjectWithLength>';
|
||||
type JSArray extends JSArgumentsObjectWithLength
|
||||
generates 'TNode<JSArray>';
|
||||
type FastJSArray extends JSArray
|
||||
generates 'TNode<JSArray>';
|
||||
type FastJSArrayForCopy extends FastJSArray
|
||||
generates 'TNode<JSArray>';
|
||||
type JSFunction extends JSObject generates 'TNode<JSFunction>';
|
||||
type JSBoundFunction extends JSObject generates 'TNode<JSBoundFunction>';
|
||||
type Callable = JSFunction|JSBoundFunction|JSProxy;
|
||||
@ -52,20 +56,14 @@ type FixedTypedArrayBase extends FixedArrayBase
|
||||
generates 'TNode<FixedTypedArrayBase>';
|
||||
type FixedTypedArray extends FixedTypedArrayBase
|
||||
generates 'TNode<FixedTypedArray>';
|
||||
type SloppyArgumentsElements extends FixedArray
|
||||
generates 'TNode<FixedArray>';
|
||||
type NumberDictionary extends HeapObject
|
||||
generates 'TNode<NumberDictionary>';
|
||||
|
||||
type NativeContextSlot generates 'TNode<IntPtrT>' constexpr 'int32_t';
|
||||
const ARRAY_JOIN_STACK_INDEX: constexpr NativeContextSlot
|
||||
generates 'Context::ARRAY_JOIN_STACK_INDEX';
|
||||
const FAST_ALIASED_ARGUMENTS_MAP_INDEX: constexpr NativeContextSlot
|
||||
generates 'Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX';
|
||||
const SLOW_ALIASED_ARGUMENTS_MAP_INDEX: constexpr NativeContextSlot
|
||||
generates 'Context::SLOW_ALIASED_ARGUMENTS_MAP_INDEX';
|
||||
const STRICT_ARGUMENTS_MAP_INDEX: constexpr NativeContextSlot
|
||||
generates 'Context::STRICT_ARGUMENTS_MAP_INDEX';
|
||||
const SLOPPY_ARGUMENTS_MAP_INDEX: constexpr NativeContextSlot
|
||||
generates 'Context::SLOPPY_ARGUMENTS_MAP_INDEX';
|
||||
extern operator '[]' macro LoadContextElement(
|
||||
NativeContext, NativeContextSlot): Object;
|
||||
extern operator '[]=' macro StoreContextElement(
|
||||
@ -177,6 +175,18 @@ const kStringMaxLength: constexpr int31 generates 'String::kMaxLength';
|
||||
const kFixedArrayMaxLength:
|
||||
constexpr int31 generates 'FixedArray::kMaxLength';
|
||||
|
||||
const kMaxRegularHeapObjectSize: constexpr int31
|
||||
generates 'kMaxRegularHeapObjectSize';
|
||||
|
||||
const kMaxNewSpaceFixedArrayElements: constexpr int31
|
||||
generates 'FixedArray::kMaxRegularLength';
|
||||
const kSloppyArgumentsArgumentsIndex: constexpr int31
|
||||
generates 'SloppyArgumentsElements::kArgumentsIndex';
|
||||
const kSloppyArgumentsContextIndex: constexpr int31
|
||||
generates 'SloppyArgumentsElements::kContextIndex';
|
||||
const kSloppyArgumentsParameterMapStart: constexpr int31
|
||||
generates 'SloppyArgumentsElements::kParameterMapStart';
|
||||
|
||||
const kTruncateMinusZero: constexpr ToIntegerTruncationMode
|
||||
generates 'ToIntegerTruncationMode::kTruncateMinusZero';
|
||||
|
||||
@ -308,6 +318,11 @@ extern macro IsElementsKindGreaterThan(
|
||||
extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool;
|
||||
extern macro IsDoubleElementsKind(constexpr ElementsKind): constexpr bool;
|
||||
|
||||
extern macro IsFastAliasedArgumentsMap(implicit context: Context)(Map): bool;
|
||||
extern macro IsSlowAliasedArgumentsMap(implicit context: Context)(Map): bool;
|
||||
extern macro IsSloppyArgumentsMap(implicit context: Context)(Map): bool;
|
||||
extern macro IsStrictArgumentsMap(implicit context: Context)(Map): bool;
|
||||
|
||||
extern macro SmiAbove(Smi, Smi): bool;
|
||||
|
||||
extern operator '==' macro WordEqual(intptr, intptr): bool;
|
||||
@ -442,6 +457,9 @@ extern macro HeapObjectToString(HeapObject): String
|
||||
labels CastError;
|
||||
extern macro HeapObjectToHeapNumber(HeapObject): HeapNumber
|
||||
labels CastError;
|
||||
extern macro HeapObjectToSloppyArgumentsElements(HeapObject):
|
||||
SloppyArgumentsElements
|
||||
labels CastError;
|
||||
extern macro TaggedToNumber(Object): Number
|
||||
labels CastError;
|
||||
|
||||
@ -459,6 +477,10 @@ CastHeapObject<FixedDoubleArray>(o: HeapObject): FixedDoubleArray
|
||||
labels CastError {
|
||||
return HeapObjectToFixedDoubleArray(o) otherwise CastError;
|
||||
}
|
||||
CastHeapObject<SloppyArgumentsElements>(o: HeapObject): SloppyArgumentsElements
|
||||
labels CastError {
|
||||
return HeapObjectToSloppyArgumentsElements(o) otherwise CastError;
|
||||
}
|
||||
CastHeapObject<JSDataView>(o: HeapObject): JSDataView
|
||||
labels CastError {
|
||||
return HeapObjectToJSDataView(o) otherwise CastError;
|
||||
@ -766,17 +788,18 @@ UnsafeCast<String>(o: Object): String {
|
||||
// type check().
|
||||
extern macro RawCastObjectToJSArgumentsObjectWithLength(Object):
|
||||
JSArgumentsObjectWithLength;
|
||||
extern macro RawCastObjectToFastJSArray(Object): FastJSArray;
|
||||
extern macro RawCastObjectToFastJSArrayForCopy(Object): FastJSArrayForCopy;
|
||||
|
||||
macro BranchIfJSArgumentsObjectWithLength(implicit context: Context)(o: Object):
|
||||
never
|
||||
labels True, False {
|
||||
const heapObject: HeapObject = Cast<HeapObject>(o) otherwise False;
|
||||
const map: Map = heapObject.map;
|
||||
const nativeContext: NativeContext = LoadNativeContext(context);
|
||||
if (map == nativeContext[FAST_ALIASED_ARGUMENTS_MAP_INDEX]) goto True;
|
||||
if (map == nativeContext[SLOW_ALIASED_ARGUMENTS_MAP_INDEX]) goto True;
|
||||
if (map == nativeContext[STRICT_ARGUMENTS_MAP_INDEX]) goto True;
|
||||
if (map != nativeContext[SLOPPY_ARGUMENTS_MAP_INDEX]) goto False;
|
||||
if (IsFastAliasedArgumentsMap(map)) goto True;
|
||||
if (IsSloppyArgumentsMap(map)) goto True;
|
||||
if (IsStrictArgumentsMap(map)) goto True;
|
||||
if (!IsSlowAliasedArgumentsMap(map)) goto False;
|
||||
goto True;
|
||||
}
|
||||
|
||||
@ -797,17 +820,59 @@ Cast<JSArgumentsObjectWithLength>(implicit context: Context)(o: Object):
|
||||
}
|
||||
}
|
||||
|
||||
UnsafeCast<FastJSArray>(implicit context: Context)(o: Object): FastJSArray {
|
||||
assert(BranchIfFastJSArrayForCopy(o, context));
|
||||
return RawCastObjectToFastJSArray(o);
|
||||
}
|
||||
|
||||
Cast<FastJSArray>(implicit context: Context)(o: Object): FastJSArray
|
||||
labels CastError {
|
||||
if (BranchIfFastJSArray(o, context)) {
|
||||
return UnsafeCast<FastJSArray>(o);
|
||||
} else {
|
||||
goto CastError;
|
||||
}
|
||||
}
|
||||
|
||||
Cast<FastJSArray>(implicit context: Context)(ho: HeapObject): FastJSArray
|
||||
labels CastError {
|
||||
if (BranchIfFastJSArray(ho, context)) {
|
||||
return UnsafeCast<FastJSArray>(ho);
|
||||
} else {
|
||||
goto CastError;
|
||||
}
|
||||
}
|
||||
|
||||
UnsafeCast<FastJSArrayForCopy>(implicit context: Context)(o: Object):
|
||||
FastJSArrayForCopy {
|
||||
assert(BranchIfFastJSArrayForCopy(o, context));
|
||||
return RawCastObjectToFastJSArrayForCopy(o);
|
||||
}
|
||||
|
||||
Cast<FastJSArrayForCopy>(implicit context: Context)(o: Object):
|
||||
FastJSArrayForCopy
|
||||
labels CastError {
|
||||
if (BranchIfFastJSArrayForCopy(o, context)) {
|
||||
return UnsafeCast<FastJSArrayForCopy>(o);
|
||||
} else {
|
||||
goto CastError;
|
||||
}
|
||||
}
|
||||
|
||||
const kCOWMap: Map = UnsafeCast<Map>(LoadRoot(kFixedCOWArrayMapRootIndex));
|
||||
const kEmptyFixedArray: FixedArrayBase =
|
||||
UnsafeCast<FixedArrayBase>(LoadRoot(kEmptyFixedArrayRootIndex));
|
||||
|
||||
extern macro BranchIfFastJSArray(Object, Context): never
|
||||
labels Taken, NotTaken;
|
||||
extern macro BranchIfFastJSArrayForCopy(Object, Context): never
|
||||
labels Taken, NotTaken;
|
||||
extern macro BranchIfNotFastJSArray(Object, Context): never
|
||||
labels Taken, NotTaken;
|
||||
|
||||
macro EnsureFastJSArray(context: Context, object: Object) labels Bailout {
|
||||
if (BranchIfNotFastJSArray(object, context)) goto Bailout;
|
||||
macro BranchIfNotFastJSArrayForCopy(implicit context: Context)(o: Object):
|
||||
never
|
||||
labels Taken, NotTaken {
|
||||
BranchIfFastJSArrayForCopy(o, context) otherwise NotTaken, Taken;
|
||||
}
|
||||
|
||||
extern macro IsPrototypeInitialArrayPrototype(Context, Map): bool;
|
||||
@ -832,7 +897,7 @@ extern operator '.length' macro LoadJSTypedArrayLength(JSTypedArray): Smi;
|
||||
extern operator '.length' macro LoadJSArrayLength(JSArray): Number;
|
||||
extern operator '.length' macro LoadJSArgumentsObjectWithLength(
|
||||
JSArgumentsObjectWithLength): Object;
|
||||
extern operator '.length_fast' macro LoadFastJSArrayLength(JSArray): Smi;
|
||||
extern operator '.length' macro LoadFastJSArrayLength(FastJSArray): Smi;
|
||||
extern operator '.length=' macro StoreJSArrayLength(JSArray, Smi);
|
||||
|
||||
extern operator '.length' macro LoadFixedArrayBaseLength(FixedArrayBase): Smi;
|
||||
@ -944,6 +1009,7 @@ extern macro Call(
|
||||
extern macro Call(
|
||||
Context, Callable, Object, Object, Object, Object, Object, Object): Object;
|
||||
|
||||
extern builtin CloneFastJSArray(Context, FastJSArrayForCopy): JSArray;
|
||||
extern macro ExtractFixedArray(FixedArrayBase, Smi, Smi, Smi): FixedArrayBase;
|
||||
extern macro ExtractFixedArray(
|
||||
FixedArrayBase, Smi, Smi, Smi,
|
||||
@ -1080,8 +1146,7 @@ macro GetLengthProperty(implicit context: Context)(o: Object): Number {
|
||||
return a.length;
|
||||
}
|
||||
case (a: JSArgumentsObjectWithLength): {
|
||||
const length: Object = a.length;
|
||||
return Cast<Smi>(length) otherwise goto ToLength(length);
|
||||
goto ToLength(a.length);
|
||||
}
|
||||
case (Object): deferred {
|
||||
goto ToLength(GetProperty(o, kLengthString));
|
||||
|
@ -4674,7 +4674,8 @@ void CodeStubAssembler::CopyElements(ElementsKind kind,
|
||||
CSA_ASSERT(this, IntPtrLessThanOrEqual(
|
||||
IntPtrAdd(src_index, length),
|
||||
LoadAndUntagFixedArrayBaseLength(src_elements)));
|
||||
CSA_ASSERT(this, WordNotEqual(dst_elements, src_elements));
|
||||
CSA_ASSERT(this, Word32Or(WordNotEqual(dst_elements, src_elements),
|
||||
WordEqual(length, IntPtrConstant(0))));
|
||||
|
||||
// The write barrier can be ignored if {dst_elements} is in new space, or if
|
||||
// the elements pointer is FixedDoubleArray.
|
||||
@ -5831,6 +5832,38 @@ TNode<BoolT> CodeStubAssembler::IsPrototypeTypedArrayPrototype(
|
||||
return WordEqual(proto_of_proto, typed_array_prototype);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsFastAliasedArgumentsMap(
|
||||
TNode<Context> context, TNode<Map> map) {
|
||||
TNode<Context> const native_context = LoadNativeContext(context);
|
||||
TNode<Object> const arguments_map = LoadContextElement(
|
||||
native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX);
|
||||
return WordEqual(arguments_map, map);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsSlowAliasedArgumentsMap(
|
||||
TNode<Context> context, TNode<Map> map) {
|
||||
TNode<Context> const native_context = LoadNativeContext(context);
|
||||
TNode<Object> const arguments_map = LoadContextElement(
|
||||
native_context, Context::SLOW_ALIASED_ARGUMENTS_MAP_INDEX);
|
||||
return WordEqual(arguments_map, map);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsSloppyArgumentsMap(TNode<Context> context,
|
||||
TNode<Map> map) {
|
||||
TNode<Context> const native_context = LoadNativeContext(context);
|
||||
TNode<Object> const arguments_map =
|
||||
LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX);
|
||||
return WordEqual(arguments_map, map);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::IsStrictArgumentsMap(TNode<Context> context,
|
||||
TNode<Map> map) {
|
||||
TNode<Context> const native_context = LoadNativeContext(context);
|
||||
TNode<Object> const arguments_map =
|
||||
LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX);
|
||||
return WordEqual(arguments_map, map);
|
||||
}
|
||||
|
||||
TNode<BoolT> CodeStubAssembler::TaggedIsCallable(TNode<Object> object) {
|
||||
return Select<BoolT>(
|
||||
TaggedIsSmi(object), [=] { return Int32FalseConstant(); },
|
||||
|
@ -423,6 +423,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
return TNode<JSArgumentsObjectWithLength>::UncheckedCast(p_o);
|
||||
}
|
||||
|
||||
TNode<JSArray> RawCastObjectToFastJSArray(TNode<Object> p_o) {
|
||||
return TNode<JSArray>::UncheckedCast(p_o);
|
||||
}
|
||||
|
||||
TNode<JSArray> RawCastObjectToFastJSArrayForCopy(TNode<Object> p_o) {
|
||||
return TNode<JSArray>::UncheckedCast(p_o);
|
||||
}
|
||||
|
||||
Node* MatchesParameterMode(Node* value, ParameterMode mode);
|
||||
|
||||
#define PARAMETER_BINOP(OpName, IntPtrOpName, SmiOpName) \
|
||||
@ -1639,6 +1647,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
return UncheckedCast<FixedDoubleArray>(base);
|
||||
}
|
||||
|
||||
TNode<FixedArray> HeapObjectToSloppyArgumentsElements(TNode<HeapObject> base,
|
||||
Label* cast_fail) {
|
||||
GotoIf(WordNotEqual(LoadMap(base),
|
||||
LoadRoot(RootIndex::kSloppyArgumentsElementsMap)),
|
||||
cast_fail);
|
||||
return UncheckedCast<FixedArray>(base);
|
||||
}
|
||||
|
||||
TNode<Int32T> ConvertElementsKindToInt(TNode<Int32T> elements_kind) {
|
||||
return UncheckedCast<Int32T>(elements_kind);
|
||||
}
|
||||
@ -2008,6 +2024,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
SloppyTNode<Map> map);
|
||||
TNode<BoolT> IsPrototypeTypedArrayPrototype(SloppyTNode<Context> context,
|
||||
SloppyTNode<Map> map);
|
||||
|
||||
TNode<BoolT> IsFastAliasedArgumentsMap(TNode<Context> context,
|
||||
TNode<Map> map);
|
||||
TNode<BoolT> IsSlowAliasedArgumentsMap(TNode<Context> context,
|
||||
TNode<Map> map);
|
||||
TNode<BoolT> IsSloppyArgumentsMap(TNode<Context> context, TNode<Map> map);
|
||||
TNode<BoolT> IsStrictArgumentsMap(TNode<Context> context, TNode<Map> map);
|
||||
|
||||
TNode<BoolT> IsSequentialStringInstanceType(
|
||||
SloppyTNode<Int32T> instance_type);
|
||||
TNode<BoolT> IsUncachedExternalStringInstanceType(
|
||||
|
@ -3439,6 +3439,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
|
||||
case Builtins::kArrayPrototypeShift:
|
||||
return ReduceArrayPrototypeShift(node);
|
||||
case Builtins::kArrayPrototypeSlice:
|
||||
case Builtins::kArraySlice:
|
||||
return ReduceArrayPrototypeSlice(node);
|
||||
case Builtins::kArrayPrototypeEntries:
|
||||
return ReduceArrayIterator(node, IterationKind::kEntries);
|
||||
|
@ -384,6 +384,8 @@ bool BuiltinToIntrinsicHasNoSideEffect(Builtins::Name builtin_id,
|
||||
/* Arrays */ \
|
||||
V(Builtins::kArrayFilter, W(CreateDataProperty)) \
|
||||
V(Builtins::kArrayMap, W(CreateDataProperty)) \
|
||||
V(Builtins::kArraySlice, \
|
||||
W(CreateDataProperty) W(SetKeyedProperty) W(SetNamedProperty)) \
|
||||
V(Builtins::kArrayPrototypeSlice, \
|
||||
W(CreateDataProperty) W(SetKeyedProperty) W(SetNamedProperty)) \
|
||||
/* TypedArrays */ \
|
||||
@ -556,6 +558,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
|
||||
case Builtins::kArrayPrototypeKeys:
|
||||
case Builtins::kArrayPrototypeLastIndexOf:
|
||||
case Builtins::kArrayPrototypeSlice:
|
||||
case Builtins::kArraySlice:
|
||||
case Builtins::kArrayPrototypeSort:
|
||||
case Builtins::kArrayPrototypeToLocaleString:
|
||||
case Builtins::kArrayPrototypeToString:
|
||||
|
@ -228,8 +228,25 @@ void DeclarationVisitor::Visit(SpecializationDeclaration* decl) {
|
||||
|
||||
if (matching_callable == nullptr) {
|
||||
std::stringstream stream;
|
||||
if (generic_list->list().size() == 0) {
|
||||
stream << "no generic defined with the name " << decl->name;
|
||||
ReportError(stream.str());
|
||||
}
|
||||
stream << "specialization of " << decl->name
|
||||
<< " doesn't match any generic declaration";
|
||||
<< " doesn't match any generic declaration\n";
|
||||
stream << "specialization signature:";
|
||||
stream << "\n " << signature_with_types;
|
||||
stream << "\ncandidates are:";
|
||||
for (Generic* generic : generic_list->list()) {
|
||||
SpecializationKey key = {generic,
|
||||
GetTypeVector(decl->generic_parameters)};
|
||||
Declarations::CleanNodeScopeActivator specialization_activator(
|
||||
declarations(), decl);
|
||||
DeclareSpecializedTypes(key);
|
||||
Signature generic_signature_with_types =
|
||||
MakeSignature(generic->declaration()->callable->signature.get());
|
||||
stream << "\n " << generic_signature_with_types;
|
||||
}
|
||||
ReportError(stream.str());
|
||||
}
|
||||
|
||||
|
@ -112,15 +112,19 @@ function array_natives_test() {
|
||||
assertEquals([2], a2.slice(1,2));
|
||||
a2 = [1.1,2,3];
|
||||
assertTrue(%HasDoubleElements(a2.slice()));
|
||||
assertTrue(%HasDoubleElements(a2.slice(1)));
|
||||
assertTrue(%HasDoubleElements(a2.slice(1, 2)));
|
||||
assertTrue(%HasDoubleElements(a2.slice(1)) ||
|
||||
%HasSmiElements(a2.slice(1)));
|
||||
assertTrue(%HasDoubleElements(a2.slice(1, 2)) ||
|
||||
%HasSmiElements(a2.slice(1, 2)));
|
||||
assertEquals([1.1,2,3], a2.slice());
|
||||
assertEquals([2,3], a2.slice(1));
|
||||
assertEquals([2], a2.slice(1,2));
|
||||
a2 = [{},2,3];
|
||||
assertTrue(%HasObjectElements(a2.slice()));
|
||||
assertTrue(%HasObjectElements(a2.slice(1)));
|
||||
assertTrue(%HasObjectElements(a2.slice(1, 2)));
|
||||
assertTrue(%HasObjectElements(a2.slice(1)) ||
|
||||
%HasSmiElements(a2.slice(1)));
|
||||
assertTrue(%HasObjectElements(a2.slice(1, 2)) ||
|
||||
%HasSmiElements(a2.slice(1, 2)));
|
||||
assertEquals([{},2,3], a2.slice());
|
||||
assertEquals([2,3], a2.slice(1));
|
||||
assertEquals([2], a2.slice(1,2));
|
||||
|
@ -2,6 +2,22 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
(function () {
|
||||
function f() {
|
||||
arguments.length = -5;
|
||||
Array.prototype.slice.call(arguments);
|
||||
}
|
||||
f('a')
|
||||
})();
|
||||
|
||||
(function () {
|
||||
function f() {
|
||||
arguments.length = 2.3;
|
||||
Array.prototype.slice.call(arguments);
|
||||
}
|
||||
f('a')
|
||||
})();
|
||||
|
||||
(function () {
|
||||
function f( __v_59960) {
|
||||
arguments.length = -5;
|
||||
@ -13,7 +29,6 @@
|
||||
(function () {
|
||||
function f( __v_59960) {
|
||||
arguments.length = 2.3;
|
||||
print(arguments.length);
|
||||
Array.prototype.slice.call(arguments);
|
||||
}
|
||||
f('a')
|
||||
|
21
third_party/v8/builtins/array-sort.tq
vendored
21
third_party/v8/builtins/array-sort.tq
vendored
@ -325,14 +325,12 @@ module array {
|
||||
builtin CanUseSameAccessor<ElementsAccessor: type>(
|
||||
context: Context, receiver: JSReceiver, initialReceiverMap: Object,
|
||||
initialReceiverLength: Number): Boolean {
|
||||
assert(IsJSArray(receiver));
|
||||
|
||||
let a: JSArray = UnsafeCast<JSArray>(receiver);
|
||||
const a: JSArray = Cast<JSArray>(receiver) otherwise unreachable;
|
||||
if (a.map != initialReceiverMap) return False;
|
||||
|
||||
assert(TaggedIsSmi(initialReceiverLength));
|
||||
let originalLength: Smi = UnsafeCast<Smi>(initialReceiverLength);
|
||||
if (a.length_fast != originalLength) return False;
|
||||
if (a.length != originalLength) return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
@ -547,13 +545,7 @@ module array {
|
||||
// Used for OOB asserts in Copy* builtins.
|
||||
macro GetReceiverLengthProperty(
|
||||
implicit context: Context)(sortState: FixedArray): Smi {
|
||||
const receiver: JSReceiver = GetReceiver(sortState);
|
||||
|
||||
if (IsJSArray(receiver)) return UnsafeCast<JSArray>(receiver).length_fast;
|
||||
|
||||
const len: Number =
|
||||
ToLength_Inline(context, GetProperty(receiver, kLengthString));
|
||||
return UnsafeCast<Smi>(len);
|
||||
return Cast<Smi>(GetLengthProperty(GetReceiver(sortState))) otherwise unreachable;
|
||||
}
|
||||
|
||||
macro CopyToTempArray(
|
||||
@ -1747,12 +1739,10 @@ module array {
|
||||
sortState[kBailoutStatusIdx] = kSuccess;
|
||||
|
||||
try {
|
||||
const a: JSArray = Cast<JSArray>(obj) otherwise Slow;
|
||||
const elementsKind: ElementsKind = map.elements_kind;
|
||||
if (!IsFastElementsKind(elementsKind)) goto Slow;
|
||||
const a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
|
||||
|
||||
// 3. Let len be ? ToLength(? Get(obj, "length")).
|
||||
const len: Smi = a.length_fast;
|
||||
const len: Smi = a.length;
|
||||
if (len < 2) return receiver;
|
||||
|
||||
// TODO(szuend): Investigate performance tradeoff of skipping this step
|
||||
@ -1762,6 +1752,7 @@ module array {
|
||||
|
||||
sortState[kInitialReceiverLengthIdx] = len;
|
||||
|
||||
const elementsKind: ElementsKind = map.elements_kind;
|
||||
if (IsDoubleElementsKind(elementsKind)) {
|
||||
InitializeSortStateAccessor<FastDoubleElements>(sortState);
|
||||
} else if (elementsKind == PACKED_SMI_ELEMENTS) {
|
||||
|
@ -45,13 +45,13 @@ def postprocess(output):
|
||||
output = re.sub(r',([\n ]*)\/\*_LABELS_HOLD_\*\/', r'\1labels', output)
|
||||
output = re.sub(r'\/\*_OPE \'([^\']+)\'\*\/', r"operator '\1'", output)
|
||||
output = re.sub(r'\/\*_TYPE\*\/(\s*)switch', r'typeswitch', output)
|
||||
output = re.sub(r'case ([^\:]+)\:\s*\/\*_TSXDEFERRED_\*\/',
|
||||
output = re.sub(r'case (\w+)\:\s*\/\*_TSXDEFERRED_\*\/',
|
||||
r'case (\1): deferred', output)
|
||||
output = re.sub(r'case ([^\:]+)\:\s*\/\*_TSX\*\/',
|
||||
output = re.sub(r'case (\w+)\:\s*\/\*_TSX\*\/',
|
||||
r'case (\1):', output)
|
||||
output = re.sub(r'case ([^\:]+)\:\s*\/\*_TSVDEFERRED_([^\:]+)\:\*\/',
|
||||
output = re.sub(r'case (\w+)\:\s*\/\*_TSVDEFERRED_([^\:]+)\:\*\/',
|
||||
r'case (\2: \1): deferred', output)
|
||||
output = re.sub(r'case ([^\:]+)\:\s*\/\*_TSV([^\:]+)\:\*\/',
|
||||
output = re.sub(r'case (\w+)\:\s*\/\*_TSV([^\:]+)\:\*\/',
|
||||
r'case (\2: \1):', output)
|
||||
output = re.sub(r'\n_GeNeRaTeS00_\s*\/\*([^@]+)@\*\/',
|
||||
r"\n generates '\1'", output)
|
||||
|
Loading…
Reference in New Issue
Block a user