Commit e8151494 authored by jgruber's avatar jgruber Committed by Commit Bot

[typedarray] Add set fast path for JSArray source arguments

This adds a fast path that avoids the runtime transition for JSArray
source arguments with {packed,holey} {smi,double} elements kinds.

The fast path currently calls straight into C and copies there using
elements accessor logic.

Local tests show a 4x speedup when copying from 1-element JSArrays.
As the source array becomes larger, the time spent copying elements
begins to dominate.

Bug: v8:3590
Change-Id: I05ebe54d7b255d0a76ad46ac11ce7cfd516b8ac8
Reviewed-on: https://chromium-review.googlesource.com/789010
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49634}
parent a2aff67f
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include "src/debug/debug.h" #include "src/debug/debug.h"
#include "src/deoptimizer.h" #include "src/deoptimizer.h"
#include "src/disassembler.h" #include "src/disassembler.h"
#include "src/elements.h"
#include "src/execution.h" #include "src/execution.h"
#include "src/ic/ic.h" #include "src/ic/ic.h"
#include "src/ic/stub-cache.h" #include "src/ic/stub-cache.h"
...@@ -1427,6 +1428,13 @@ ExternalReference ExternalReference::jsreceiver_create_identity_hash( ...@@ -1427,6 +1428,13 @@ ExternalReference ExternalReference::jsreceiver_create_identity_hash(
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(f))); return ExternalReference(Redirect(isolate, FUNCTION_ADDR(f)));
} }
ExternalReference
ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
Isolate* isolate) {
return ExternalReference(Redirect(
isolate, FUNCTION_ADDR(CopyFastNumberJSArrayElementsToTypedArray)));
}
ExternalReference ExternalReference::try_internalize_string_function( ExternalReference ExternalReference::try_internalize_string_function(
Isolate* isolate) { Isolate* isolate) {
return ExternalReference(Redirect( return ExternalReference(Redirect(
......
...@@ -988,6 +988,9 @@ class ExternalReference BASE_EMBEDDED { ...@@ -988,6 +988,9 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference get_or_create_hash_raw(Isolate* isolate); static ExternalReference get_or_create_hash_raw(Isolate* isolate);
static ExternalReference jsreceiver_create_identity_hash(Isolate* isolate); static ExternalReference jsreceiver_create_identity_hash(Isolate* isolate);
static ExternalReference copy_fast_number_jsarray_elements_to_typed_array(
Isolate* isolate);
static ExternalReference page_flags(Page* page); static ExternalReference page_flags(Page* page);
static ExternalReference ForDeoptEntry(Address entry); static ExternalReference ForDeoptEntry(Address entry);
......
...@@ -55,9 +55,20 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { ...@@ -55,9 +55,20 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
// Fast path for setting a TypedArray (source) onto another TypedArray // Fast path for setting a TypedArray (source) onto another TypedArray
// (target) at an element offset. Currently, only handles when the source and // (target) at an element offset. Currently, only handles when the source and
// target types match. // target types match.
void SetTypedArraySource(TNode<Context> context, TNode<HeapObject> source, void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source,
TNode<HeapObject> target, TNode<IntPtrT> offset, TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* if_not_same_type); Label* call_runtime, Label* if_source_too_large);
void SetJSArraySource(TNode<Context> context, TNode<JSArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset,
Label* call_runtime, Label* if_source_too_large);
void CallCMemmove(TNode<IntPtrT> dest_ptr, TNode<IntPtrT> src_ptr,
TNode<IntPtrT> byte_length);
void CallCCopyFastNumberJSArrayElementsToTypedArray(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length, TNode<IntPtrT> offset);
}; };
Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) {
...@@ -746,24 +757,19 @@ TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize( ...@@ -746,24 +757,19 @@ TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
return element_size; return element_size;
} }
void TypedArrayBuiltinsAssembler::SetTypedArraySource(TNode<Context> context, void TypedArrayBuiltinsAssembler::SetTypedArraySource(
TNode<HeapObject> source, TNode<Context> context, TNode<JSTypedArray> source,
TNode<HeapObject> target, TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
TNode<IntPtrT> offset, Label* if_source_too_large) {
Label* if_not_same_type) {
CSA_ASSERT(this, IsJSTypedArray(source));
CSA_ASSERT(this, IsJSTypedArray(target));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0))); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this, CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue))); IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
Label next(this), if_source_too_large(this, Label::kDeferred);
// TODO(pwong): Widen this fast path. See CopyElementsHandleFromTypedArray for // TODO(pwong): Widen this fast path. See CopyElementsHandleFromTypedArray for
// the corresponding check in runtime. // the corresponding check in runtime.
TNode<Word32T> source_el_kind = LoadElementsKind(source); TNode<Word32T> source_el_kind = LoadElementsKind(source);
TNode<Word32T> target_el_kind = LoadElementsKind(target); TNode<Word32T> target_el_kind = LoadElementsKind(target);
GotoIfNot(Word32Equal(source_el_kind, target_el_kind), if_not_same_type); GotoIfNot(Word32Equal(source_el_kind, target_el_kind), call_runtime);
TNode<IntPtrT> source_byte_length = TNode<IntPtrT> source_byte_length =
LoadAndUntagObjectField(source, JSTypedArray::kByteLengthOffset); LoadAndUntagObjectField(source, JSTypedArray::kByteLengthOffset);
...@@ -773,29 +779,84 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(TNode<Context> context, ...@@ -773,29 +779,84 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(TNode<Context> context,
TNode<IntPtrT> required_byte_length = TNode<IntPtrT> required_byte_length =
IntPtrAdd(IntPtrMul(offset, source_el_size), source_byte_length); IntPtrAdd(IntPtrMul(offset, source_el_size), source_byte_length);
GotoIf(IntPtrGreaterThan(required_byte_length, target_byte_length), GotoIf(IntPtrGreaterThan(required_byte_length, target_byte_length),
&if_source_too_large); if_source_too_large);
// If source and target are the same TypedArray type. // If source and target are the same TypedArray type.
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind);
TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target));
TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source));
TNode<IntPtrT> target_start =
IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size));
CallCMemmove(target_start, source_data_ptr, source_byte_length);
}
void TypedArrayBuiltinsAssembler::SetJSArraySource(
TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target,
TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) {
CSA_ASSERT(this, IsFastJSArray(source, context));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0)));
CSA_ASSERT(this,
IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue)));
TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source));
TNode<IntPtrT> target_length =
LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset);
// Maybe out of bounds?
GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length),
if_source_too_large);
// Nothing to do if {source} is empty.
Label out(this), fast_c_call(this);
GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out);
// Dispatch based on the source elements kind.
{ {
TNode<ExternalReference> memmove = // These are the supported elements kinds in TryCopyElementsFastNumber.
ExternalConstant(ExternalReference::libc_memmove_function(isolate())); int32_t values[] = {
TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind); PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
TNode<IntPtrT> target_data_ptr = HOLEY_DOUBLE_ELEMENTS,
UncheckedCast<IntPtrT>(LoadDataPtr(target)); };
TNode<IntPtrT> source_data_ptr = Label* labels[] = {
UncheckedCast<IntPtrT>(LoadDataPtr(source)); &fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call,
TNode<IntPtrT> target_start = };
IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size)); STATIC_ASSERT(arraysize(values) == arraysize(labels));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::Pointer(), MachineType::UintPtr(), memmove, TNode<Int32T> source_elements_kind = LoadMapElementsKind(LoadMap(source));
target_start, source_data_ptr, source_byte_length); Switch(source_elements_kind, call_runtime, values, labels,
Goto(&next); arraysize(values));
} }
BIND(&if_source_too_large); BIND(&fast_c_call);
ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge); CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target,
source_length, offset);
Goto(&out);
BIND(&out);
}
BIND(&next); void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<IntPtrT> dest_ptr,
TNode<IntPtrT> src_ptr,
TNode<IntPtrT> byte_length) {
TNode<ExternalReference> memmove =
ExternalConstant(ExternalReference::libc_memmove_function(isolate()));
CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
MachineType::Pointer(), MachineType::UintPtr(), memmove,
dest_ptr, src_ptr, byte_length);
}
void TypedArrayBuiltinsAssembler::
CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context,
TNode<JSArray> source,
TNode<JSTypedArray> dest,
TNode<IntPtrT> source_length,
TNode<IntPtrT> offset) {
TNode<ExternalReference> f = ExternalConstant(
ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
isolate()));
CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::UintPtr(), MachineType::UintPtr(), f, context,
source, dest, source_length, offset);
} }
// ES #sec-get-%typedarray%.prototype.set // ES #sec-get-%typedarray%.prototype.set
...@@ -804,7 +865,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) { ...@@ -804,7 +865,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
CodeStubArguments args( CodeStubArguments args(
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount))); this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
Label if_offset_is_out_of_bounds(this, Label::kDeferred), Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this),
if_offset_is_out_of_bounds(this, Label::kDeferred),
if_source_too_large(this, Label::kDeferred),
if_receiver_is_neutered(this, Label::kDeferred), if_receiver_is_neutered(this, Label::kDeferred),
if_receiver_is_not_typedarray(this, Label::kDeferred); if_receiver_is_not_typedarray(this, Label::kDeferred);
...@@ -833,16 +896,28 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) { ...@@ -833,16 +896,28 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
Label call_runtime(this); Label call_runtime(this);
TNode<Object> source = args.GetOptionalArgumentValue(0); TNode<Object> source = args.GetOptionalArgumentValue(0);
GotoIf(TaggedIsSmi(source), &call_runtime); GotoIf(TaggedIsSmi(source), &call_runtime);
GotoIfNot(IsJSTypedArray(source), &call_runtime); GotoIf(IsJSTypedArray(source), &if_source_is_typed_array);
BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray,
&call_runtime);
// Fast path for a typed array source argument. // Fast path for a typed array source argument.
BIND(&if_source_is_typed_array);
{ {
CSA_ASSERT(this, IsJSTypedArray(source));
SetTypedArraySource(context, CAST(source), CAST(receiver), SetTypedArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime); SmiUntag(offset_smi), &call_runtime,
&if_source_too_large);
args.PopAndReturn(UndefinedConstant());
}
// Fast path for a fast JSArray source argument.
BIND(&if_source_is_fast_jsarray);
{
SetJSArraySource(context, CAST(source), CAST(receiver),
SmiUntag(offset_smi), &call_runtime, &if_source_too_large);
args.PopAndReturn(UndefinedConstant()); args.PopAndReturn(UndefinedConstant());
} }
// TODO(pwong): This is an opportunity to add a fast path for fast JS Array.
BIND(&call_runtime); BIND(&call_runtime);
args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver, args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver,
source, offset_smi)); source, offset_smi));
...@@ -850,6 +925,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) { ...@@ -850,6 +925,9 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
BIND(&if_offset_is_out_of_bounds); BIND(&if_offset_is_out_of_bounds);
ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds); ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds);
BIND(&if_source_too_large);
ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge);
BIND(&if_receiver_is_neutered); BIND(&if_receiver_is_neutered);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, ThrowTypeError(context, MessageTemplate::kDetachedOperation,
"%TypedArray%.prototype.set"); "%TypedArray%.prototype.set");
......
...@@ -1184,6 +1184,16 @@ Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters( ...@@ -1184,6 +1184,16 @@ Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters(
mode); mode);
} }
Node* CodeAssembler::CallCFunction5(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3,
Node* arg4) {
return raw_assembler()->CallCFunction5(
return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type,
function, arg0, arg1, arg2, arg3, arg4);
}
Node* CodeAssembler::CallCFunction6( Node* CodeAssembler::CallCFunction6(
MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
......
...@@ -1006,6 +1006,13 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -1006,6 +1006,13 @@ class V8_EXPORT_PRIVATE CodeAssembler {
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
SaveFPRegsMode mode); SaveFPRegsMode mode);
// Call to a C function with five arguments.
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2,
Node* arg3, Node* arg4);
// Call to a C function with six arguments. // Call to a C function with six arguments.
Node* CallCFunction6(MachineType return_type, MachineType arg0_type, Node* CallCFunction6(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type, MachineType arg1_type, MachineType arg2_type,
......
...@@ -301,6 +301,25 @@ Node* RawMachineAssembler::CallCFunction3WithCallerSavedRegisters( ...@@ -301,6 +301,25 @@ Node* RawMachineAssembler::CallCFunction3WithCallerSavedRegisters(
arg0, arg1, arg2); arg0, arg1, arg2);
} }
Node* RawMachineAssembler::CallCFunction5(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3,
Node* arg4) {
MachineSignature::Builder builder(zone(), 1, 5);
builder.AddReturn(return_type);
builder.AddParam(arg0_type);
builder.AddParam(arg1_type);
builder.AddParam(arg2_type);
builder.AddParam(arg3_type);
builder.AddParam(arg4_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
return AddNode(common()->Call(descriptor), function, arg0, arg1, arg2, arg3,
arg4);
}
Node* RawMachineAssembler::CallCFunction6( Node* RawMachineAssembler::CallCFunction6(
MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
......
...@@ -778,6 +778,12 @@ class V8_EXPORT_PRIVATE RawMachineAssembler { ...@@ -778,6 +778,12 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2,
SaveFPRegsMode mode = kSaveFPRegs); SaveFPRegsMode mode = kSaveFPRegs);
// Call to a C function with five arguments.
Node* CallCFunction5(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, MachineType arg4_type,
Node* function, Node* arg0, Node* arg1, Node* arg2,
Node* arg3, Node* arg4);
// Call to a C function with six arguments. // Call to a C function with six arguments.
Node* CallCFunction6(MachineType return_type, MachineType arg0_type, Node* CallCFunction6(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type, MachineType arg1_type, MachineType arg2_type,
......
...@@ -3269,23 +3269,27 @@ class TypedElementsAccessor ...@@ -3269,23 +3269,27 @@ class TypedElementsAccessor
} }
} }
static bool HoleyPrototypeLookupRequired(Isolate* isolate, static bool HoleyPrototypeLookupRequired(Isolate* isolate, Context* context,
Handle<JSArray> source) { JSArray* source) {
DisallowHeapAllocation no_gc;
DisallowJavascriptExecution no_js(isolate);
Object* source_proto = source->map()->prototype(); Object* source_proto = source->map()->prototype();
// Null prototypes are OK - we don't need to do prototype chain lookups on // Null prototypes are OK - we don't need to do prototype chain lookups on
// them. // them.
if (source_proto->IsNull(isolate)) return false; if (source_proto->IsNull(isolate)) return false;
if (source_proto->IsJSProxy()) return true; if (source_proto->IsJSProxy()) return true;
DCHECK(source_proto->IsJSObject()); if (!context->is_initial_array_prototype(JSObject::cast(source_proto))) {
if (!isolate->is_initial_array_prototype(JSObject::cast(source_proto))) {
return true; return true;
} }
return !isolate->IsNoElementsProtectorIntact();
return !isolate->IsNoElementsProtectorIntact(context);
} }
static bool TryCopyElementsHandleFastNumber(Handle<JSArray> source, static bool TryCopyElementsFastNumber(Context* context, JSArray* source,
Handle<JSTypedArray> destination, JSTypedArray* destination,
size_t length, uint32_t offset) { size_t length, uint32_t offset) {
Isolate* isolate = source->GetIsolate(); Isolate* isolate = source->GetIsolate();
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
DisallowJavascriptExecution no_js(isolate); DisallowJavascriptExecution no_js(isolate);
...@@ -3298,7 +3302,7 @@ class TypedElementsAccessor ...@@ -3298,7 +3302,7 @@ class TypedElementsAccessor
// When the array has the original array prototype, and that prototype has // When the array has the original array prototype, and that prototype has
// not been changed in a way that would affect lookups, we can just convert // not been changed in a way that would affect lookups, we can just convert
// the hole into undefined. // the hole into undefined.
if (HoleyPrototypeLookupRequired(isolate, source)) return false; if (HoleyPrototypeLookupRequired(isolate, context, source)) return false;
Object* undefined = isolate->heap()->undefined_value(); Object* undefined = isolate->heap()->undefined_value();
...@@ -3408,8 +3412,8 @@ class TypedElementsAccessor ...@@ -3408,8 +3412,8 @@ class TypedElementsAccessor
// Fast cases for packed numbers kinds where we don't need to allocate. // Fast cases for packed numbers kinds where we don't need to allocate.
if (source->IsJSArray()) { if (source->IsJSArray()) {
Handle<JSArray> source_array = Handle<JSArray>::cast(source); Handle<JSArray> source_array = Handle<JSArray>::cast(source);
if (TryCopyElementsHandleFastNumber(source_array, destination_ta, length, if (TryCopyElementsFastNumber(isolate->context(), *source_array,
offset)) { *destination_ta, length, offset)) {
return *isolate->factory()->undefined_value(); return *isolate->factory()->undefined_value();
} }
} }
...@@ -4357,6 +4361,27 @@ MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array, ...@@ -4357,6 +4361,27 @@ MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array,
return array; return array;
} }
void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSArray* source,
JSTypedArray* destination,
uintptr_t length,
uintptr_t offset) {
DCHECK(context->IsContext());
DCHECK(source->IsJSArray());
DCHECK(destination->IsJSTypedArray());
switch (destination->GetElementsKind()) {
#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
CHECK(Fixed##Type##ElementsAccessor::TryCopyElementsFastNumber( \
context, source, destination, length, static_cast<uint32_t>(offset))); \
break;
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
#undef TYPED_ARRAYS_CASE
default:
UNREACHABLE();
}
}
void ElementsAccessor::InitializeOncePerProcess() { void ElementsAccessor::InitializeOncePerProcess() {
static ElementsAccessor* accessor_array[] = { static ElementsAccessor* accessor_array[] = {
......
...@@ -237,6 +237,14 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements( ...@@ -237,6 +237,14 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements(
Handle<JSArray> array, Handle<JSArray> array,
Arguments* args); Arguments* args);
// Called directly from CSA.
class JSTypedArray;
void CopyFastNumberJSArrayElementsToTypedArray(Context* context,
JSArray* source,
JSTypedArray* destination,
uintptr_t length,
uintptr_t offset);
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -270,6 +270,10 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) { ...@@ -270,6 +270,10 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"get_or_create_hash_raw"); "get_or_create_hash_raw");
Add(ExternalReference::jsreceiver_create_identity_hash(isolate).address(), Add(ExternalReference::jsreceiver_create_identity_hash(isolate).address(),
"jsreceiver_create_identity_hash"); "jsreceiver_create_identity_hash");
Add(ExternalReference::copy_fast_number_jsarray_elements_to_typed_array(
isolate)
.address(),
"copy_fast_number_jsarray_elements_to_typed_array");
Add(ExternalReference::log_enter_external_function(isolate).address(), Add(ExternalReference::log_enter_external_function(isolate).address(),
"Logger::EnterExternal"); "Logger::EnterExternal");
Add(ExternalReference::log_leave_external_function(isolate).address(), Add(ExternalReference::log_leave_external_function(isolate).address(),
......
...@@ -3127,16 +3127,17 @@ bool Isolate::IsInAnyContext(Object* object, uint32_t index) { ...@@ -3127,16 +3127,17 @@ bool Isolate::IsInAnyContext(Object* object, uint32_t index) {
return false; return false;
} }
bool Isolate::IsNoElementsProtectorIntact() { bool Isolate::IsNoElementsProtectorIntact(Context* context) {
PropertyCell* no_elements_cell = heap()->no_elements_protector(); PropertyCell* no_elements_cell = heap()->no_elements_protector();
bool cell_reports_intact = bool cell_reports_intact =
no_elements_cell->value()->IsSmi() && no_elements_cell->value()->IsSmi() &&
Smi::ToInt(no_elements_cell->value()) == kProtectorValid; Smi::ToInt(no_elements_cell->value()) == kProtectorValid;
#ifdef DEBUG #ifdef DEBUG
Context* native_context = context->native_context();
Map* root_array_map = Map* root_array_map =
raw_native_context()->GetInitialJSArrayMap(GetInitialFastElementsKind()); native_context->GetInitialJSArrayMap(GetInitialFastElementsKind());
Context* native_context = context()->native_context();
JSObject* initial_array_proto = JSObject::cast( JSObject* initial_array_proto = JSObject::cast(
native_context->get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX)); native_context->get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX));
JSObject* initial_object_proto = JSObject::cast( JSObject* initial_object_proto = JSObject::cast(
...@@ -3177,13 +3178,16 @@ bool Isolate::IsNoElementsProtectorIntact() { ...@@ -3177,13 +3178,16 @@ bool Isolate::IsNoElementsProtectorIntact() {
PrototypeIterator iter(this, initial_array_proto); PrototypeIterator iter(this, initial_array_proto);
if (iter.IsAtEnd() || iter.GetCurrent() != initial_object_proto) { if (iter.IsAtEnd() || iter.GetCurrent() != initial_object_proto) {
DCHECK_EQ(false, cell_reports_intact); DCHECK_EQ(false, cell_reports_intact);
DCHECK(!has_pending_exception());
return cell_reports_intact; return cell_reports_intact;
} }
iter.Advance(); iter.Advance();
if (!iter.IsAtEnd()) { if (!iter.IsAtEnd()) {
DCHECK_EQ(false, cell_reports_intact); DCHECK_EQ(false, cell_reports_intact);
DCHECK(!has_pending_exception());
return cell_reports_intact; return cell_reports_intact;
} }
DCHECK(!has_pending_exception());
// Check that the String.prototype hasn't been altered WRT empty elements. // Check that the String.prototype hasn't been altered WRT empty elements.
elements = initial_string_proto->elements(); elements = initial_string_proto->elements();
...@@ -3199,12 +3203,15 @@ bool Isolate::IsNoElementsProtectorIntact() { ...@@ -3199,12 +3203,15 @@ bool Isolate::IsNoElementsProtectorIntact() {
DCHECK_EQ(false, cell_reports_intact); DCHECK_EQ(false, cell_reports_intact);
return cell_reports_intact; return cell_reports_intact;
} }
#endif #endif
return cell_reports_intact; return cell_reports_intact;
} }
bool Isolate::IsNoElementsProtectorIntact() {
return Isolate::IsNoElementsProtectorIntact(context());
}
bool Isolate::IsIsConcatSpreadableLookupChainIntact() { bool Isolate::IsIsConcatSpreadableLookupChainIntact() {
Cell* is_concat_spreadable_cell = heap()->is_concat_spreadable_protector(); Cell* is_concat_spreadable_cell = heap()->is_concat_spreadable_protector();
bool is_is_concat_spreadable_set = bool is_is_concat_spreadable_set =
......
...@@ -1084,7 +1084,13 @@ class Isolate { ...@@ -1084,7 +1084,13 @@ class Isolate {
static const int kProtectorInvalid = 0; static const int kProtectorInvalid = 0;
inline bool IsArrayConstructorIntact(); inline bool IsArrayConstructorIntact();
// The version with an explicit context parameter can be used when
// Isolate::context is not set up, e.g. when calling directly into C++ from
// CSA.
bool IsNoElementsProtectorIntact(Context* context);
bool IsNoElementsProtectorIntact(); bool IsNoElementsProtectorIntact();
inline bool IsArraySpeciesLookupChainIntact(); inline bool IsArraySpeciesLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(); bool IsIsConcatSpreadableLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver); bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
......
...@@ -900,6 +900,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) { ...@@ -900,6 +900,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) {
return isolate->heap()->ToBoolean(obj->Has##Name()); \ return isolate->heap()->ToBoolean(obj->Has##Name()); \
} }
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ObjectElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ObjectElements)
ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiOrObjectElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiOrObjectElements)
......
...@@ -594,6 +594,7 @@ namespace internal { ...@@ -594,6 +594,7 @@ namespace internal {
F(TraceExit, 1, 1) \ F(TraceExit, 1, 1) \
F(HaveSameMap, 2, 1) \ F(HaveSameMap, 2, 1) \
F(InNewSpace, 1, 1) \ F(InNewSpace, 1, 1) \
F(HasFastElements, 1, 1) \
F(HasSmiElements, 1, 1) \ F(HasSmiElements, 1, 1) \
F(HasObjectElements, 1, 1) \ F(HasObjectElements, 1, 1) \
F(HasSmiOrObjectElements, 1, 1) \ F(HasSmiOrObjectElements, 1, 1) \
......
...@@ -636,6 +636,73 @@ function TestTypedArraySet() { ...@@ -636,6 +636,73 @@ function TestTypedArraySet() {
}; };
assertThrows(() => Int8Array.prototype.set.call(1, tmp), TypeError); assertThrows(() => Int8Array.prototype.set.call(1, tmp), TypeError);
assertThrows(() => Int8Array.prototype.set.call([], tmp), TypeError); assertThrows(() => Int8Array.prototype.set.call([], tmp), TypeError);
// Detached array buffer when converting offset.
{
const xs = new Int8Array(10);
let detached = false;
const offset = {
[Symbol.toPrimitive]() {
%ArrayBufferNeuter(xs.buffer);
detached = true;
return 0;
}
};
assertThrows(() => xs.set(xs, offset), TypeError);
assertEquals(true, detached);
}
// Various offset edge cases.
{
const xs = new Int8Array(10);
assertThrows(() => xs.set(xs, -1), RangeError);
assertThrows(() => xs.set(xs, -1 * 2**64), RangeError);
xs.set(xs, -0.0);
xs.set(xs, 0.0);
xs.set(xs, 0.5);
assertThrows(() => xs.set(xs, 2**64), RangeError);
}
// Exhaustively test elements kind combinations with JSArray source arg.
{
const kSize = 3;
const targets = typedArrayConstructors.map(klass => new klass(kSize));
const sources = [ [0,1,2] // PACKED_SMI
, [0,,2] // HOLEY_SMI
, [0.1,0.2,0.3] // PACKED_DOUBLE
, [0.1,,0.3] // HOLEY_DOUBLE
, [{},{},{}] // PACKED
, [{},,{}] // HOLEY
, [] // DICTIONARY (patched later)
];
// Migrate to DICTIONARY_ELEMENTS.
Object.defineProperty(sources[6], 0, {});
assertTrue(%HasSmiElements(sources[0]));
assertTrue(%HasFastElements(sources[0]) && !%HasHoleyElements(sources[0]));
assertTrue(%HasSmiElements(sources[1]));
assertTrue(%HasFastElements(sources[1]) && %HasHoleyElements(sources[1]));
assertTrue(%HasDoubleElements(sources[2]));
assertTrue(%HasFastElements(sources[2]) && !%HasHoleyElements(sources[2]));
assertTrue(%HasDoubleElements(sources[3]));
assertTrue(%HasFastElements(sources[3]) && %HasHoleyElements(sources[3]));
assertTrue(%HasObjectElements(sources[4]));
assertTrue(%HasFastElements(sources[4]) && !%HasHoleyElements(sources[4]));
assertTrue(%HasObjectElements(sources[4]));
assertTrue(%HasFastElements(sources[4]) && !%HasHoleyElements(sources[4]));
assertTrue(%HasObjectElements(sources[5]));
assertTrue(%HasFastElements(sources[5]) && %HasHoleyElements(sources[5]));
assertTrue(%HasDictionaryElements(sources[6]));
for (const target of targets) {
for (const source of sources) {
target.set(source);
%HeapObjectVerify(target);
%HeapObjectVerify(source);
}
}
}
} }
TestTypedArraySet(); TestTypedArraySet();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment