[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:
Daniel Clifford 2018-10-19 10:32:19 +02:00 committed by Commit Bot
parent 2d8adf3db2
commit 41ba3d3eb0
18 changed files with 502 additions and 59 deletions

View File

@ -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",

View 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");

View File

@ -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,

View File

@ -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;

View File

@ -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
View 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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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));

View File

@ -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(); },

View File

@ -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(

View File

@ -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);

View File

@ -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:

View File

@ -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());
}

View File

@ -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));

View File

@ -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')

View File

@ -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) {

View File

@ -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)