[typedarray] Migrate %TypedArray%.of to CSA
- Remove %TypedArray%.of in js/typedarray.js - Implement %TypedArray%.of in builtins/builtins-typedarray-gen.cc - This CL makes TA.of 1.5x faster Bug: v8:5929 Change-Id: Ie165114a0ab9b4ec9ed70840c4c6a42d1eeed101 Reviewed-on: https://chromium-review.googlesource.com/897227 Commit-Queue: Peter Marshall <petermarshall@chromium.org> Reviewed-by: Peter Marshall <petermarshall@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#51141}
This commit is contained in:
parent
f4e42f9d31
commit
756c8c4e1b
@ -3004,6 +3004,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
InstallSpeciesGetter(typed_array_fun);
|
||||
native_context()->set_typed_array_function(*typed_array_fun);
|
||||
|
||||
SimpleInstallFunction(typed_array_fun, "of", Builtins::kTypedArrayOf, 0,
|
||||
false);
|
||||
|
||||
// Setup %TypedArrayPrototype%.
|
||||
Handle<JSObject> prototype(
|
||||
JSObject::cast(typed_array_fun->instance_prototype()));
|
||||
|
@ -1125,6 +1125,8 @@ namespace internal {
|
||||
/* ES6 %TypedArray%.prototype.forEach */ \
|
||||
TFJ(TypedArrayPrototypeForEach, \
|
||||
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 %TypedArray%.of */ \
|
||||
TFJ(TypedArrayOf, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
\
|
||||
/* Wasm */ \
|
||||
ASM(WasmCompileLazy) \
|
||||
|
@ -926,6 +926,12 @@ TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByLength(
|
||||
TNode<Object> constructor = TypedArraySpeciesConstructor(context, exemplar);
|
||||
CSA_ASSERT(this, IsJSFunction(constructor));
|
||||
|
||||
return CreateByLength(context, constructor, len, method_name);
|
||||
}
|
||||
|
||||
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::CreateByLength(
|
||||
TNode<Context> context, TNode<Object> constructor, TNode<Smi> len,
|
||||
const char* method_name) {
|
||||
// Let newTypedArray be ? Construct(constructor, argumentList).
|
||||
TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
|
||||
context, constructor, len));
|
||||
@ -1544,6 +1550,89 @@ TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
|
||||
context, receiver, "%TypedArray%.prototype.keys()", IterationKind::kKeys);
|
||||
}
|
||||
|
||||
void TypedArrayBuiltinsAssembler::DebugSanityCheckTypedArrayIndex(
|
||||
TNode<JSTypedArray> array, SloppyTNode<Smi> index) {
|
||||
#ifdef DEBUG
|
||||
TNode<JSArrayBuffer> buffer =
|
||||
LoadObjectField<JSArrayBuffer>(array, JSArrayBufferView::kBufferOffset);
|
||||
CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer(buffer)));
|
||||
TNode<Smi> array_length =
|
||||
LoadObjectField<Smi>(array, JSTypedArray::kLengthOffset);
|
||||
CSA_ASSERT(this, SmiLessThan(index, array_length));
|
||||
#endif
|
||||
}
|
||||
|
||||
// ES6 #sec-%typedarray%.of
|
||||
TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
|
||||
|
||||
// 1. Let len be the actual number of arguments passed to this function.
|
||||
TNode<Int32T> argc =
|
||||
UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
|
||||
TNode<Smi> length = SmiFromWord32(argc);
|
||||
// 2. Let items be the List of arguments passed to this function.
|
||||
CodeStubArguments args(this, length, nullptr, ParameterMode::SMI_PARAMETERS,
|
||||
CodeStubArguments::ReceiverMode::kHasReceiver);
|
||||
|
||||
Label if_not_constructor(this, Label::kDeferred),
|
||||
unreachable(this, Label::kDeferred);
|
||||
|
||||
// 3. Let C be the this value.
|
||||
// 4. If IsConstructor(C) is false, throw a TypeError exception.
|
||||
TNode<Object> receiver = args.GetReceiver();
|
||||
GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
|
||||
GotoIfNot(IsConstructor(receiver), &if_not_constructor);
|
||||
|
||||
// 5. Let newObj be ? TypedArrayCreate(C, len).
|
||||
TNode<JSTypedArray> new_typed_array =
|
||||
CreateByLength(context, receiver, length, "%TypedArray%.of");
|
||||
|
||||
TNode<Word32T> elements_kind = LoadElementsKind(new_typed_array);
|
||||
|
||||
// 6. Let k be 0.
|
||||
// 7. Repeat, while k < len
|
||||
// a. Let kValue be items[k].
|
||||
// b. Let Pk be ! ToString(k).
|
||||
// c. Perform ? Set(newObj, Pk, kValue, true).
|
||||
// d. Increase k by 1.
|
||||
DispatchTypedArrayByElementsKind(
|
||||
elements_kind,
|
||||
[&](ElementsKind kind, int size, int typed_array_fun_index) {
|
||||
BuildFastLoop(
|
||||
SmiConstant(0), length,
|
||||
[&](Node* index) {
|
||||
TNode<Object> item =
|
||||
args.AtIndex(index, ParameterMode::SMI_PARAMETERS);
|
||||
TNode<Number> number = ToNumber_Inline(context, item);
|
||||
|
||||
// ToNumber may execute JavaScript code, but it cannot access
|
||||
// arguments array and new typed array.
|
||||
DebugSanityCheckTypedArrayIndex(new_typed_array, index);
|
||||
|
||||
// Since we can guarantee that "number" is Number type,
|
||||
// PrepareValueForWriteToTypedArray cannot bail out.
|
||||
Node* value =
|
||||
PrepareValueForWriteToTypedArray(number, kind, &unreachable);
|
||||
|
||||
// GC may move backing store in ToNumber, thus load backing store
|
||||
// everytime in this loop.
|
||||
TNode<IntPtrT> backing_store =
|
||||
UncheckedCast<IntPtrT>(LoadDataPtr(new_typed_array));
|
||||
StoreElement(backing_store, kind, index, value, SMI_PARAMETERS);
|
||||
},
|
||||
1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
|
||||
});
|
||||
|
||||
// 8. Return newObj.
|
||||
args.PopAndReturn(new_typed_array);
|
||||
|
||||
BIND(&unreachable);
|
||||
Unreachable();
|
||||
|
||||
BIND(&if_not_constructor);
|
||||
ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
|
||||
}
|
||||
|
||||
#undef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP
|
||||
|
||||
} // namespace internal
|
||||
|
@ -80,6 +80,10 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
TNode<Smi> len,
|
||||
const char* method_name);
|
||||
|
||||
TNode<JSTypedArray> CreateByLength(TNode<Context> context,
|
||||
TNode<Object> constructor, TNode<Smi> len,
|
||||
const char* method_name);
|
||||
|
||||
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
|
||||
TNode<JSTypedArray> array);
|
||||
|
||||
@ -113,6 +117,9 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
|
||||
void DispatchTypedArrayByElementsKind(
|
||||
TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function);
|
||||
|
||||
void DebugSanityCheckTypedArrayIndex(TNode<JSTypedArray> array,
|
||||
SloppyTNode<Smi> index);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -5611,6 +5611,30 @@ TNode<Numeric> CodeStubAssembler::NonNumberToNumeric(
|
||||
return UncheckedCast<Numeric>(result);
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::ToNumber_Inline(TNode<Context> context,
|
||||
TNode<Object> input) {
|
||||
TVARIABLE(Number, var_result);
|
||||
Label end(this), not_smi(this, Label::kDeferred);
|
||||
|
||||
GotoIfNot(TaggedIsSmi(input), ¬_smi);
|
||||
var_result = CAST(input);
|
||||
Goto(&end);
|
||||
|
||||
BIND(¬_smi);
|
||||
{
|
||||
var_result = Select<Number>(
|
||||
IsHeapNumber(input), [=] { return CAST(input); },
|
||||
[=] {
|
||||
return CallBuiltin(Builtins::kNonNumberToNumeric, context, input);
|
||||
},
|
||||
MachineRepresentation::kTagged);
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
BIND(&end);
|
||||
return var_result;
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::ToNumber(SloppyTNode<Context> context,
|
||||
SloppyTNode<Object> input,
|
||||
BigIntHandling bigint_handling) {
|
||||
|
@ -1240,6 +1240,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
TNode<Number> ToNumber(
|
||||
SloppyTNode<Context> context, SloppyTNode<Object> input,
|
||||
BigIntHandling bigint_handling = BigIntHandling::kThrow);
|
||||
TNode<Number> ToNumber_Inline(TNode<Context> context, TNode<Object> input);
|
||||
|
||||
// Converts |input| to one of 2^32 integer values in the range 0 through
|
||||
// 2^32-1, inclusive.
|
||||
|
@ -203,20 +203,6 @@ DEFINE_METHOD(
|
||||
);
|
||||
|
||||
|
||||
// ES6 draft 08-24-14, section 22.2.2.2
|
||||
DEFINE_METHOD(
|
||||
GlobalTypedArray,
|
||||
of() {
|
||||
var length = arguments.length;
|
||||
var array = TypedArrayCreate(this, length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
array[i] = arguments[i];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// ES#sec-iterabletoarraylike Runtime Semantics: IterableToArrayLike( items )
|
||||
function IterableToArrayLike(items) {
|
||||
var iterable = GetMethod(items, iteratorSymbol);
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright 2014 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.
|
||||
//
|
||||
// Flags: --expose-gc
|
||||
|
||||
// Based on Mozilla Array.of() tests at http://dxr.mozilla.org/mozilla-central/source/js/src/jit-test/tests/collections
|
||||
|
||||
@ -130,6 +132,20 @@ function TestTypedArrayOf(constructor) {
|
||||
for (var x of [undefined, null, false, true, "cow", 42, 3.14]) {
|
||||
assertThrows(function () { constructor.of.call(x); }, TypeError);
|
||||
}
|
||||
|
||||
// Check if it's correctly accessing new typed array elements even after
|
||||
// garbage collection is invoked in ToNumber.
|
||||
var not_number = {
|
||||
[Symbol.toPrimitive]() {
|
||||
gc();
|
||||
return 123;
|
||||
}
|
||||
};
|
||||
var dangerous_array = new Array(64).fill(not_number);
|
||||
var a = constructor.of(...dangerous_array);
|
||||
for (var i = 0; i < 64; i++) {
|
||||
assertEquals(123, a[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var constructor of typedArrayConstructors) {
|
||||
|
Loading…
Reference in New Issue
Block a user