From 76b8f25edbea72df2e58b86eeaf201cfbc86eea1 Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Wed, 26 Mar 2014 12:50:13 +0000 Subject: [PATCH] This implements allocating small typed arrays in heap. R=mvstanton@chromium.org, verwaest@chromium.org Review URL: https://codereview.chromium.org/150813004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20279 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 2 +- src/accessors.cc | 8 +- src/api.cc | 15 ++- src/bootstrapper.cc | 2 +- src/elements-kind.cc | 19 +++- src/elements-kind.h | 15 ++- src/flag-definitions.h | 3 + src/heap.cc | 41 ++++++- src/heap.h | 16 +++ src/hydrogen.cc | 246 +++++++++++++++++++++++++++++++---------- src/hydrogen.h | 13 +++ src/objects-debug.cc | 3 +- src/objects-inl.h | 129 +++++++++++++++++---- src/objects.cc | 127 ++++++++++++++++----- src/objects.h | 30 ++++- src/runtime.cc | 111 +++++++++++++++---- src/runtime.h | 17 ++- src/typedarray.js | 8 +- test/mjsunit/elements-kind.js | 113 +++++++++++++------ test/mjsunit/external-array.js | 1 + 20 files changed, 732 insertions(+), 187 deletions(-) diff --git a/include/v8.h b/include/v8.h index 6590ed5..608e3c5 100644 --- a/include/v8.h +++ b/include/v8.h @@ -5571,7 +5571,7 @@ class Internals { static const int kNullValueRootIndex = 7; static const int kTrueValueRootIndex = 8; static const int kFalseValueRootIndex = 9; - static const int kEmptyStringRootIndex = 145; + static const int kEmptyStringRootIndex = 154; static const int kNodeClassIdOffset = 1 * kApiPointerSize; static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3; diff --git a/src/accessors.cc b/src/accessors.cc index dda1dee..35cff1a 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -119,9 +119,7 @@ bool Accessors::IsJSObjectFieldAccessor(typename T::TypeHandle type, CheckForName(name, isolate->heap()->byte_length_string(), JSTypedArray::kByteLengthOffset, object_offset) || CheckForName(name, isolate->heap()->byte_offset_string(), - JSTypedArray::kByteOffsetOffset, object_offset) || - CheckForName(name, isolate->heap()->buffer_string(), - JSTypedArray::kBufferOffset, object_offset); + JSTypedArray::kByteOffsetOffset, object_offset); case JS_ARRAY_BUFFER_TYPE: return CheckForName(name, isolate->heap()->byte_length_string(), @@ -131,9 +129,7 @@ bool Accessors::IsJSObjectFieldAccessor(typename T::TypeHandle type, CheckForName(name, isolate->heap()->byte_length_string(), JSDataView::kByteLengthOffset, object_offset) || CheckForName(name, isolate->heap()->byte_offset_string(), - JSDataView::kByteOffsetOffset, object_offset) || - CheckForName(name, isolate->heap()->buffer_string(), - JSDataView::kBufferOffset, object_offset); + JSDataView::kByteOffsetOffset, object_offset); default: return false; } diff --git a/src/api.cc b/src/api.cc index f61f190..2530ac6 100644 --- a/src/api.cc +++ b/src/api.cc @@ -6018,8 +6018,15 @@ Local v8::ArrayBuffer::New(Isolate* isolate, void* data, Local v8::ArrayBufferView::Buffer() { i::Handle obj = Utils::OpenHandle(this); - ASSERT(obj->buffer()->IsJSArrayBuffer()); - i::Handle buffer(i::JSArrayBuffer::cast(obj->buffer())); + i::Handle buffer; + if (obj->IsJSDataView()) { + i::Handle data_view(i::JSDataView::cast(*obj)); + ASSERT(data_view->buffer()->IsJSArrayBuffer()); + buffer = i::handle(i::JSArrayBuffer::cast(data_view->buffer())); + } else { + ASSERT(obj->IsJSTypedArray()); + buffer = i::JSTypedArray::cast(*obj)->GetBuffer(); + } return Utils::ToLocal(buffer); } @@ -6090,7 +6097,9 @@ i::Handle NewTypedArray( isolate->factory()->NewExternalArray( static_cast(length), array_type, static_cast(buffer->backing_store()) + byte_offset); - obj->set_elements(*elements); + i::Handle map = + i::JSObject::GetElementsTransitionMap(obj, elements_kind); + obj->set_map_and_elements(*map, *elements); return obj; } diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index b2a52cd..4a931a4 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1099,7 +1099,7 @@ void Genesis::InitializeGlobal(Handle inner_global, #define INSTALL_TYPED_ARRAY(Type, type, TYPE, ctype, size) \ { \ Handle fun = InstallTypedArray(#Type "Array", \ - EXTERNAL_##TYPE##_ELEMENTS); \ + TYPE##_ELEMENTS); \ native_context()->set_##type##_array_fun(*fun); \ } TYPED_ARRAYS(INSTALL_TYPED_ARRAY) diff --git a/src/elements-kind.cc b/src/elements-kind.cc index c9480d6..ff458e0 100644 --- a/src/elements-kind.cc +++ b/src/elements-kind.cc @@ -142,14 +142,27 @@ int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind) { } +ElementsKind GetNextTransitionElementsKind(ElementsKind kind) { + switch (kind) { +#define FIXED_TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: return EXTERNAL_##TYPE##_ELEMENTS; + + TYPED_ARRAYS(FIXED_TYPED_ARRAY_CASE) +#undef FIXED_TYPED_ARRAY_CASE + default: { + int index = GetSequenceIndexFromFastElementsKind(kind); + return GetFastElementsKindFromSequenceIndex(index + 1); + } + } +} + + ElementsKind GetNextMoreGeneralFastElementsKind(ElementsKind elements_kind, bool allow_only_packed) { ASSERT(IsFastElementsKind(elements_kind)); ASSERT(elements_kind != TERMINAL_FAST_ELEMENTS_KIND); while (true) { - int index = - GetSequenceIndexFromFastElementsKind(elements_kind) + 1; - elements_kind = GetFastElementsKindFromSequenceIndex(index); + elements_kind = GetNextTransitionElementsKind(elements_kind); if (!IsFastHoleyElementsKind(elements_kind) || !allow_only_packed) { return elements_kind; } diff --git a/src/elements-kind.h b/src/elements-kind.h index 32108f5..d2605e8 100644 --- a/src/elements-kind.h +++ b/src/elements-kind.h @@ -100,10 +100,10 @@ void PrintElementsKind(FILE* out, ElementsKind kind); ElementsKind GetInitialFastElementsKind(); -ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_index); - +ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_number); int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind); +ElementsKind GetNextTransitionElementsKind(ElementsKind elements_kind); inline bool IsDictionaryElementsKind(ElementsKind kind) { return kind == DICTIONARY_ELEMENTS; @@ -116,6 +116,12 @@ inline bool IsExternalArrayElementsKind(ElementsKind kind) { } +inline bool IsTerminalElementsKind(ElementsKind kind) { + return kind == TERMINAL_FAST_ELEMENTS_KIND || + IsExternalArrayElementsKind(kind); +} + + inline bool IsFixedTypedArrayElementsKind(ElementsKind kind) { return kind >= FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND && kind <= LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND; @@ -128,6 +134,11 @@ inline bool IsFastElementsKind(ElementsKind kind) { } +inline bool IsTransitionElementsKind(ElementsKind kind) { + return IsFastElementsKind(kind) || IsFixedTypedArrayElementsKind(kind); +} + + inline bool IsFastDoubleElementsKind(ElementsKind kind) { return kind == FAST_DOUBLE_ELEMENTS || kind == FAST_HOLEY_DOUBLE_ELEMENTS; diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 2d2cadf..3f0f7ec 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -357,6 +357,9 @@ DEFINE_bool(omit_map_checks_for_leaf_maps, true, "do not emit check maps for constant values that have a leaf map, " "deoptimize the optimized code if the layout of the maps changes.") +DEFINE_int(typed_array_max_size_in_heap, 64, + "threshold for in-heap typed array") + // Profiler flags. DEFINE_int(frame_count, 1, "number of stack frames inspected by the profiler") // 0x1800 fits in the immediate field of an ARM instruction. diff --git a/src/heap.cc b/src/heap.cc index eae7241..8d321ad 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2934,6 +2934,16 @@ bool Heap::CreateInitialMaps() { TYPED_ARRAYS(ALLOCATE_EMPTY_EXTERNAL_ARRAY) #undef ALLOCATE_EMPTY_EXTERNAL_ARRAY + +#define ALLOCATE_EMPTY_FIXED_TYPED_ARRAY(Type, type, TYPE, ctype, size) \ + { FixedTypedArrayBase* obj; \ + if (!AllocateEmptyFixedTypedArray(kExternal##Type##Array)->To(&obj)) \ + return false; \ + set_empty_fixed_##type##_array(obj); \ + } + + TYPED_ARRAYS(ALLOCATE_EMPTY_FIXED_TYPED_ARRAY) +#undef ALLOCATE_EMPTY_FIXED_TYPED_ARRAY } ASSERT(!InNewSpace(empty_fixed_array())); return true; @@ -3762,12 +3772,34 @@ Heap::RootListIndex Heap::RootIndexForEmptyExternalArray( } +Heap::RootListIndex Heap::RootIndexForEmptyFixedTypedArray( + ElementsKind elementsKind) { + switch (elementsKind) { +#define ELEMENT_KIND_TO_ROOT_INDEX(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: \ + return kEmptyFixed##Type##ArrayRootIndex; + + TYPED_ARRAYS(ELEMENT_KIND_TO_ROOT_INDEX) +#undef ELEMENT_KIND_TO_ROOT_INDEX + default: + UNREACHABLE(); + return kUndefinedValueRootIndex; + } +} + + ExternalArray* Heap::EmptyExternalArrayForMap(Map* map) { return ExternalArray::cast( roots_[RootIndexForEmptyExternalArray(map->elements_kind())]); } +FixedTypedArrayBase* Heap::EmptyFixedTypedArrayForMap(Map* map) { + return FixedTypedArrayBase::cast( + roots_[RootIndexForEmptyFixedTypedArray(map->elements_kind())]); +} + + MaybeObject* Heap::NumberFromDouble(double value, PretenureFlag pretenure) { // We need to distinguish the minus zero value and this cannot be // done after conversion to int. Doing this by comparing bit @@ -4063,6 +4095,7 @@ MaybeObject* Heap::AllocateFixedTypedArray(int length, reinterpret_cast(object); elements->set_map(MapForFixedTypedArray(array_type)); elements->set_length(length); + memset(elements->DataPtr(), 0, elements->DataSize()); return elements; } @@ -4471,7 +4504,8 @@ MaybeObject* Heap::AllocateJSObjectFromMap( // Initialize the JSObject. InitializeJSObjectFromMap(JSObject::cast(obj), properties, map); ASSERT(JSObject::cast(obj)->HasFastElements() || - JSObject::cast(obj)->HasExternalArrayElements()); + JSObject::cast(obj)->HasExternalArrayElements() || + JSObject::cast(obj)->HasFixedTypedArrayElements()); return obj; } @@ -5139,6 +5173,11 @@ MaybeObject* Heap::CopyAndTenureFixedCOWArray(FixedArray* src) { } +MaybeObject* Heap::AllocateEmptyFixedTypedArray(ExternalArrayType array_type) { + return AllocateFixedTypedArray(0, array_type, TENURED); +} + + MaybeObject* Heap::CopyFixedArrayWithMap(FixedArray* src, Map* map) { int len = src->length(); Object* obj; diff --git a/src/heap.h b/src/heap.h index fde95ce..0f586e9 100644 --- a/src/heap.h +++ b/src/heap.h @@ -164,6 +164,16 @@ namespace internal { V(Map, fixed_float32_array_map, FixedFloat32ArrayMap) \ V(Map, fixed_float64_array_map, FixedFloat64ArrayMap) \ V(Map, fixed_uint8_clamped_array_map, FixedUint8ClampedArrayMap) \ + V(FixedTypedArrayBase, empty_fixed_uint8_array, EmptyFixedUint8Array) \ + V(FixedTypedArrayBase, empty_fixed_int8_array, EmptyFixedInt8Array) \ + V(FixedTypedArrayBase, empty_fixed_uint16_array, EmptyFixedUint16Array) \ + V(FixedTypedArrayBase, empty_fixed_int16_array, EmptyFixedInt16Array) \ + V(FixedTypedArrayBase, empty_fixed_uint32_array, EmptyFixedUint32Array) \ + V(FixedTypedArrayBase, empty_fixed_int32_array, EmptyFixedInt32Array) \ + V(FixedTypedArrayBase, empty_fixed_float32_array, EmptyFixedFloat32Array) \ + V(FixedTypedArrayBase, empty_fixed_float64_array, EmptyFixedFloat64Array) \ + V(FixedTypedArrayBase, empty_fixed_uint8_clamped_array, \ + EmptyFixedUint8ClampedArray) \ V(Map, sloppy_arguments_elements_map, SloppyArgumentsElementsMap) \ V(Map, function_context_map, FunctionContextMap) \ V(Map, catch_context_map, CatchContextMap) \ @@ -1655,7 +1665,9 @@ class Heap { ExternalArrayType array_type); RootListIndex RootIndexForEmptyExternalArray(ElementsKind kind); + RootListIndex RootIndexForEmptyFixedTypedArray(ElementsKind kind); ExternalArray* EmptyExternalArrayForMap(Map* map); + FixedTypedArrayBase* EmptyFixedTypedArrayForMap(Map* map); void RecordStats(HeapStats* stats, bool take_snapshot = false); @@ -2221,6 +2233,10 @@ class Heap { MUST_USE_RESULT MaybeObject* AllocateEmptyExternalArray( ExternalArrayType array_type); + // Allocate empty fixed typed array of given type. + MUST_USE_RESULT MaybeObject* AllocateEmptyFixedTypedArray( + ExternalArrayType array_type); + // Allocate empty fixed double array. MUST_USE_RESULT MaybeObject* AllocateEmptyFixedDoubleArray(); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index c292a17..3472874 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1904,6 +1904,19 @@ void HGraphBuilder::BuildCopySeqStringChars(HValue* src, } +HValue* HGraphBuilder::BuildObjectSizeAlignment( + HValue* unaligned_size, int header_size) { + ASSERT((header_size & kObjectAlignmentMask) == 0); + HValue* size = AddUncasted( + unaligned_size, Add(static_cast( + header_size + kObjectAlignmentMask))); + size->ClearFlag(HValue::kCanOverflow); + return AddUncasted( + Token::BIT_AND, size, Add(static_cast( + ~kObjectAlignmentMask))); +} + + HValue* HGraphBuilder::BuildUncheckedStringAdd( HValue* left, HValue* right, @@ -2004,13 +2017,7 @@ HValue* HGraphBuilder::BuildUncheckedStringAdd( // Calculate the number of bytes needed for the characters in the // string while observing object alignment. STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0); - HValue* size = Pop(); - size = AddUncasted(size, Add(static_cast( - SeqString::kHeaderSize + kObjectAlignmentMask))); - size->ClearFlag(HValue::kCanOverflow); - size = AddUncasted( - Token::BIT_AND, size, Add(static_cast( - ~kObjectAlignmentMask))); + HValue* size = BuildObjectSizeAlignment(Pop(), SeqString::kHeaderSize); // Allocate the string object. HAllocate does not care whether we pass // STRING_TYPE or ASCII_STRING_TYPE here, so we just use STRING_TYPE here. @@ -6470,7 +6477,8 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( access = AddInstruction(BuildKeyedGeneric(access_type, object, key, val)); } else { ASSERT(IsFastElementsKind(elements_kind) || - IsExternalArrayElementsKind(elements_kind)); + IsExternalArrayElementsKind(elements_kind) || + IsFixedTypedArrayElementsKind(elements_kind)); LoadKeyedHoleMode load_mode = BuildKeyedHoleMode(map); // Happily, mapcompare is a checked object. access = BuildUncheckedMonomorphicElementAccess( @@ -8423,9 +8431,6 @@ void HGraphBuilder::BuildArrayBufferViewInitialization( Add( obj, - HObjectAccess::ForJSArrayBufferViewBuffer(), buffer); - Add( - obj, HObjectAccess::ForJSArrayBufferViewByteOffset(), byte_offset); Add( @@ -8433,14 +8438,27 @@ void HGraphBuilder::BuildArrayBufferViewInitialization( HObjectAccess::ForJSArrayBufferViewByteLength(), byte_length); - HObjectAccess weak_first_view_access = - HObjectAccess::ForJSArrayBufferWeakFirstView(); - Add(obj, - HObjectAccess::ForJSArrayBufferViewWeakNext(), - Add(buffer, static_cast(NULL), - weak_first_view_access)); - Add( - buffer, weak_first_view_access, obj); + if (buffer != NULL) { + Add( + obj, + HObjectAccess::ForJSArrayBufferViewBuffer(), buffer); + HObjectAccess weak_first_view_access = + HObjectAccess::ForJSArrayBufferWeakFirstView(); + Add(obj, + HObjectAccess::ForJSArrayBufferViewWeakNext(), + Add(buffer, + static_cast(NULL), + weak_first_view_access)); + Add(buffer, weak_first_view_access, obj); + } else { + Add( + obj, + HObjectAccess::ForJSArrayBufferViewBuffer(), + Add(static_cast(0))); + Add(obj, + HObjectAccess::ForJSArrayBufferViewWeakNext(), + graph()->GetConstantUndefined()); + } } @@ -8467,6 +8485,115 @@ void HOptimizedGraphBuilder::GenerateDataViewInitialize( } +static Handle TypedArrayMap(Isolate* isolate, + ExternalArrayType array_type, + ElementsKind target_kind) { + Handle native_context = isolate->native_context(); + Handle fun; + switch (array_type) { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case kExternal##Type##Array: \ + fun = Handle(native_context->type##_array_fun()); \ + break; + + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + } + Handle map(fun->initial_map()); + return Map::AsElementsKind(map, target_kind); +} + + +HValue* HOptimizedGraphBuilder::BuildAllocateExternalElements( + ExternalArrayType array_type, + bool is_zero_byte_offset, + HValue* buffer, HValue* byte_offset, HValue* length) { + Handle external_array_map( + isolate()->heap()->MapForExternalArrayType(array_type)); + HValue* elements = + Add( + Add(ExternalArray::kAlignedSize), + HType::Tagged(), + NOT_TENURED, + external_array_map->instance_type()); + + AddStoreMapConstant(elements, external_array_map); + + HValue* backing_store = Add( + buffer, static_cast(NULL), + HObjectAccess::ForJSArrayBufferBackingStore()); + + HValue* typed_array_start; + if (is_zero_byte_offset) { + typed_array_start = backing_store; + } else { + HInstruction* external_pointer = + AddUncasted(backing_store, byte_offset); + // Arguments are checked prior to call to TypedArrayInitialize, + // including byte_offset. + external_pointer->ClearFlag(HValue::kCanOverflow); + typed_array_start = external_pointer; + } + + + Add(elements, + HObjectAccess::ForExternalArrayExternalPointer(), + typed_array_start); + + Add(elements, + HObjectAccess::ForFixedArrayLength(), length); + return elements; +} + + +HValue* HOptimizedGraphBuilder::BuildAllocateFixedTypedArray( + ExternalArrayType array_type, size_t element_size, + ElementsKind fixed_elements_kind, + HValue* byte_length, HValue* length) { + STATIC_ASSERT( + (FixedTypedArrayBase::kHeaderSize & kObjectAlignmentMask) == 0); + HValue* total_size; + + // if fixed array's elements are not aligned to object's alignment, + // we need to align the whole array to object alignment. + if (element_size % kObjectAlignment != 0) { + total_size = BuildObjectSizeAlignment( + byte_length, FixedTypedArrayBase::kHeaderSize); + } else { + total_size = AddUncasted(byte_length, + Add(FixedTypedArrayBase::kHeaderSize)); + total_size->ClearFlag(HValue::kCanOverflow); + } + + Handle fixed_typed_array_map( + isolate()->heap()->MapForFixedTypedArray(array_type)); + HValue* elements = + Add(total_size, HType::Tagged(), + NOT_TENURED, + fixed_typed_array_map->instance_type()); + AddStoreMapConstant(elements, fixed_typed_array_map); + + Add(elements, + HObjectAccess::ForFixedArrayLength(), + length); + HValue* filler = Add(static_cast(0)); + + { + LoopBuilder builder(this, context(), LoopBuilder::kPostIncrement); + + HValue* key = builder.BeginBody( + Add(static_cast(0)), + length, Token::LT); + Add(elements, key, filler, fixed_elements_kind); + + builder.EndBody(); + } + Add( + elements, HObjectAccess::ForFixedArrayLength(), length); + return elements; +} + + void HOptimizedGraphBuilder::GenerateTypedArrayInitialize( CallRuntime* expr) { ZoneList* arguments = expr->arguments(); @@ -8490,8 +8617,13 @@ void HOptimizedGraphBuilder::GenerateTypedArrayInitialize( ASSERT(value->IsSmi()); int array_id = Smi::cast(*value)->value(); - CHECK_ALIVE(VisitForValue(arguments->at(kBufferArg))); - HValue* buffer = Pop(); + HValue* buffer; + if (!arguments->at(kBufferArg)->IsNullLiteral()) { + CHECK_ALIVE(VisitForValue(arguments->at(kBufferArg))); + buffer = Pop(); + } else { + buffer = NULL; + } HValue* byte_offset; bool is_zero_byte_offset; @@ -8505,6 +8637,7 @@ void HOptimizedGraphBuilder::GenerateTypedArrayInitialize( CHECK_ALIVE(VisitForValue(arguments->at(kByteOffsetArg))); byte_offset = Pop(); is_zero_byte_offset = false; + ASSERT(buffer != NULL); } CHECK_ALIVE(VisitForValue(arguments->at(kByteLengthArg))); @@ -8517,13 +8650,24 @@ void HOptimizedGraphBuilder::GenerateTypedArrayInitialize( byte_offset_smi.Then(); } + ExternalArrayType array_type = + kExternalInt8Array; // Bogus initialization. + size_t element_size = 1; // Bogus initialization. + ElementsKind external_elements_kind = // Bogus initialization. + EXTERNAL_INT8_ELEMENTS; + ElementsKind fixed_elements_kind = // Bogus initialization. + INT8_ELEMENTS; + Runtime::ArrayIdToTypeAndSize(array_id, + &array_type, + &external_elements_kind, + &fixed_elements_kind, + &element_size); + + { // byte_offset is Smi. BuildArrayBufferViewInitialization( obj, buffer, byte_offset, byte_length); - ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization. - size_t element_size = 1; // Bogus initialization. - Runtime::ArrayIdToTypeAndSize(array_id, &array_type, &element_size); HInstruction* length = AddUncasted(byte_length, Add(static_cast(element_size))); @@ -8532,40 +8676,19 @@ void HOptimizedGraphBuilder::GenerateTypedArrayInitialize( HObjectAccess::ForJSTypedArrayLength(), length); - Handle external_array_map( - isolate()->heap()->MapForExternalArrayType(array_type)); - - HValue* elements = - Add( - Add(ExternalArray::kAlignedSize), - HType::Tagged(), - NOT_TENURED, - external_array_map->instance_type()); - - AddStoreMapConstant(elements, external_array_map); - - HValue* backing_store = Add( - buffer, static_cast(NULL), - HObjectAccess::ForJSArrayBufferBackingStore()); - - HValue* typed_array_start; - if (is_zero_byte_offset) { - typed_array_start = backing_store; + HValue* elements; + if (buffer != NULL) { + elements = BuildAllocateExternalElements( + array_type, is_zero_byte_offset, buffer, byte_offset, length); + Handle obj_map = TypedArrayMap( + isolate(), array_type, external_elements_kind); + AddStoreMapConstant(obj, obj_map); } else { - HInstruction* external_pointer = - AddUncasted(backing_store, byte_offset); - // Arguments are checked prior to call to TypedArrayInitialize, - // including byte_offset. - external_pointer->ClearFlag(HValue::kCanOverflow); - typed_array_start = external_pointer; - } - - Add(elements, - HObjectAccess::ForExternalArrayExternalPointer(), - typed_array_start); - Add(elements, - HObjectAccess::ForFixedArrayLength(), - length); + ASSERT(is_zero_byte_offset); + elements = BuildAllocateFixedTypedArray( + array_type, element_size, fixed_elements_kind, + byte_length, length); + } Add( obj, HObjectAccess::ForElementsPointer(), elements); } @@ -8593,6 +8716,15 @@ void HOptimizedGraphBuilder::GenerateMaxSmi(CallRuntime* expr) { } +void HOptimizedGraphBuilder::GenerateTypedArrayMaxSizeInHeap( + CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); + HConstant* result = New(static_cast( + FLAG_typed_array_max_size_in_heap)); + return ast_context()->ReturnInstruction(result, expr->id()); +} + + void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); diff --git a/src/hydrogen.h b/src/hydrogen.h index 7fcadea..6d81307 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -1372,6 +1372,10 @@ class HGraphBuilder { HValue* dst_offset, String::Encoding dst_encoding, HValue* length); + + // Align an object size to object alignment boundary + HValue* BuildObjectSizeAlignment(HValue* unaligned_size, int header_size); + // Both operands are non-empty strings. HValue* BuildUncheckedStringAdd(HValue* left, HValue* right, @@ -2322,6 +2326,15 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { SmallMapList* types, Handle name); + HValue* BuildAllocateExternalElements( + ExternalArrayType array_type, + bool is_zero_byte_offset, + HValue* buffer, HValue* byte_offset, HValue* length); + HValue* BuildAllocateFixedTypedArray( + ExternalArrayType array_type, size_t element_size, + ElementsKind fixed_elements_kind, + HValue* byte_length, HValue* length); + bool IsCallNewArrayInlineable(CallNew* expr); void BuildInlinedCallNewArray(CallNew* expr); diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 5ca07d5..ca025e6 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -774,7 +774,8 @@ void JSArrayBufferView::JSArrayBufferViewVerify() { CHECK(IsJSArrayBufferView()); JSObjectVerify(); VerifyPointer(buffer()); - CHECK(buffer()->IsJSArrayBuffer() || buffer()->IsUndefined()); + CHECK(buffer()->IsJSArrayBuffer() || buffer()->IsUndefined() + || buffer() == Smi::FromInt(0)); VerifyPointer(byte_offset()); CHECK(byte_offset()->IsSmi() || byte_offset()->IsHeapNumber() diff --git a/src/objects-inl.h b/src/objects-inl.h index c358ee6..cad7b63 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1736,6 +1736,11 @@ void JSObject::initialize_elements() { ExternalArray* empty_array = GetHeap()->EmptyExternalArrayForMap(map()); ASSERT(!GetHeap()->InNewSpace(empty_array)); WRITE_FIELD(this, kElementsOffset, empty_array); + } else if (map()->has_fixed_typed_array_elements()) { + FixedTypedArrayBase* empty_array = + GetHeap()->EmptyFixedTypedArrayForMap(map()); + ASSERT(!GetHeap()->InNewSpace(empty_array)); + WRITE_FIELD(this, kElementsOffset, empty_array); } else { UNREACHABLE(); } @@ -3676,35 +3681,64 @@ void ExternalFloat64Array::set(int index, double value) { } -int FixedTypedArrayBase::size() { +void* FixedTypedArrayBase::DataPtr() { + return FIELD_ADDR(this, kDataOffset); +} + + +int FixedTypedArrayBase::DataSize() { InstanceType instance_type = map()->instance_type(); int element_size; switch (instance_type) { - case FIXED_UINT8_ARRAY_TYPE: - case FIXED_INT8_ARRAY_TYPE: - case FIXED_UINT8_CLAMPED_ARRAY_TYPE: - element_size = 1; - break; - case FIXED_UINT16_ARRAY_TYPE: - case FIXED_INT16_ARRAY_TYPE: - element_size = 2; - break; - case FIXED_UINT32_ARRAY_TYPE: - case FIXED_INT32_ARRAY_TYPE: - case FIXED_FLOAT32_ARRAY_TYPE: - element_size = 4; - break; - case FIXED_FLOAT64_ARRAY_TYPE: - element_size = 8; +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case FIXED_##TYPE##_ARRAY_TYPE: \ + element_size = size; \ break; + + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE default: UNREACHABLE(); return 0; } - return OBJECT_POINTER_ALIGN(kDataOffset + length() * element_size); + return length() * element_size; +} + + +int FixedTypedArrayBase::size() { + return OBJECT_POINTER_ALIGN(kDataOffset + DataSize()); +} + + +uint8_t Uint8ArrayTraits::defaultValue() { return 0; } + + +uint8_t Uint8ClampedArrayTraits::defaultValue() { return 0; } + + +int8_t Int8ArrayTraits::defaultValue() { return 0; } + + +uint16_t Uint16ArrayTraits::defaultValue() { return 0; } + + +int16_t Int16ArrayTraits::defaultValue() { return 0; } + + +uint32_t Uint32ArrayTraits::defaultValue() { return 0; } + + +int32_t Int32ArrayTraits::defaultValue() { return 0; } + + +float Float32ArrayTraits::defaultValue() { + return static_cast(OS::nan_value()); } +double Float64ArrayTraits::defaultValue() { return OS::nan_value(); } + + template typename Traits::ElementType FixedTypedArray::get_scalar(int index) { ASSERT((index >= 0) && (index < this->length())); @@ -3740,6 +3774,47 @@ void FixedTypedArray::set( template +typename Traits::ElementType FixedTypedArray::from_int(int value) { + return static_cast(value); +} + + +template <> inline +uint8_t FixedTypedArray::from_int(int value) { + if (value < 0) return 0; + if (value > 0xFF) return 0xFF; + return static_cast(value); +} + + +template +typename Traits::ElementType FixedTypedArray::from_double( + double value) { + return static_cast(DoubleToInt32(value)); +} + + +template<> inline +uint8_t FixedTypedArray::from_double(double value) { + if (value < 0) return 0; + if (value > 0xFF) return 0xFF; + return static_cast(lrint(value)); +} + + +template<> inline +float FixedTypedArray::from_double(double value) { + return static_cast(value); +} + + +template<> inline +double FixedTypedArray::from_double(double value) { + return value; +} + + +template MaybeObject* FixedTypedArray::get(int index) { return Traits::ToObject(GetHeap(), get_scalar(index)); } @@ -3750,10 +3825,10 @@ MaybeObject* FixedTypedArray::SetValue(uint32_t index, Object* value) { if (index < static_cast(length())) { if (value->IsSmi()) { int int_value = Smi::cast(value)->value(); - cast_value = static_cast(int_value); + cast_value = from_int(int_value); } else if (value->IsHeapNumber()) { double double_value = HeapNumber::cast(value)->value(); - cast_value = static_cast(DoubleToInt32(double_value)); + cast_value = from_double(double_value); } else { // Clamp undefined to the default value. All other types have been // converted to a number type further up in the call chain. @@ -6021,6 +6096,20 @@ bool JSObject::HasFixedTypedArrayElements() { } +#define FIXED_TYPED_ELEMENTS_CHECK(Type, type, TYPE, ctype, size) \ +bool JSObject::HasFixed##Type##Elements() { \ + HeapObject* array = elements(); \ + ASSERT(array != NULL); \ + if (!array->IsHeapObject()) \ + return false; \ + return array->map()->instance_type() == FIXED_##TYPE##_ARRAY_TYPE; \ +} + +TYPED_ARRAYS(FIXED_TYPED_ELEMENTS_CHECK) + +#undef FIXED_TYPED_ELEMENTS_CHECK + + bool JSObject::HasNamedInterceptor() { return map()->has_named_interceptor(); } diff --git a/src/objects.cc b/src/objects.cc index b5e78d3..0013e72 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -3233,24 +3233,31 @@ Handle Map::FindTransitionedMap(MapHandleList* candidates) { static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) { Map* current_map = map; - int index = GetSequenceIndexFromFastElementsKind(map->elements_kind()); - int to_index = IsFastElementsKind(to_kind) - ? GetSequenceIndexFromFastElementsKind(to_kind) - : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); + int target_kind = + IsFastElementsKind(to_kind) || IsExternalArrayElementsKind(to_kind) + ? to_kind + : TERMINAL_FAST_ELEMENTS_KIND; - ASSERT(index <= to_index); + // Support for legacy API. + if (IsExternalArrayElementsKind(to_kind) && + !IsFixedTypedArrayElementsKind(map->elements_kind())) { + return map; + } - for (; index < to_index; ++index) { + ElementsKind kind = map->elements_kind(); + while (kind != target_kind) { + kind = GetNextTransitionElementsKind(kind); if (!current_map->HasElementsTransition()) return current_map; current_map = current_map->elements_transition_map(); } - if (!IsFastElementsKind(to_kind) && current_map->HasElementsTransition()) { + + if (to_kind != kind && current_map->HasElementsTransition()) { + ASSERT(to_kind == DICTIONARY_ELEMENTS); Map* next_map = current_map->elements_transition_map(); if (next_map->elements_kind() == to_kind) return next_map; } - ASSERT(IsFastElementsKind(to_kind) - ? current_map->elements_kind() == to_kind - : current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND); + + ASSERT(current_map->elements_kind() == target_kind); return current_map; } @@ -3278,26 +3285,21 @@ bool Map::IsMapInArrayPrototypeChain() { static MaybeObject* AddMissingElementsTransitions(Map* map, ElementsKind to_kind) { - ASSERT(IsFastElementsKind(map->elements_kind())); - int index = GetSequenceIndexFromFastElementsKind(map->elements_kind()); - int to_index = IsFastElementsKind(to_kind) - ? GetSequenceIndexFromFastElementsKind(to_kind) - : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); - - ASSERT(index <= to_index); + ASSERT(IsTransitionElementsKind(map->elements_kind())); Map* current_map = map; - for (; index < to_index; ++index) { - ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(index + 1); + ElementsKind kind = map->elements_kind(); + while (kind != to_kind && !IsTerminalElementsKind(kind)) { + kind = GetNextTransitionElementsKind(kind); MaybeObject* maybe_next_map = - current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION); + current_map->CopyAsElementsKind(kind, INSERT_TRANSITION); if (!maybe_next_map->To(¤t_map)) return maybe_next_map; } // In case we are exiting the fast elements kind system, just add the map in // the end. - if (!IsFastElementsKind(to_kind)) { + if (kind != to_kind) { MaybeObject* maybe_next_map = current_map->CopyAsElementsKind(to_kind, INSERT_TRANSITION); if (!maybe_next_map->To(¤t_map)) return maybe_next_map; @@ -3329,7 +3331,7 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { // Only remember the map transition if there is not an already existing // non-matching element transition. !start_map->IsUndefined() && !start_map->is_shared() && - IsFastElementsKind(from_kind); + IsTransitionElementsKind(from_kind); // Only store fast element maps in ascending generality. if (IsFastElementsKind(to_kind)) { @@ -4701,7 +4703,8 @@ static Handle CopyFastElementsToDictionary( Handle JSObject::NormalizeElements( Handle object) { - ASSERT(!object->HasExternalArrayElements()); + ASSERT(!object->HasExternalArrayElements() && + !object->HasFixedTypedArrayElements()); Isolate* isolate = object->GetIsolate(); Factory* factory = isolate->factory(); @@ -5436,7 +5439,8 @@ Handle JSObject::PreventExtensions(Handle object) { } // It's not possible to seal objects with external array elements - if (object->HasExternalArrayElements()) { + if (object->HasExternalArrayElements() || + object->HasFixedTypedArrayElements()) { Handle error = isolate->factory()->NewTypeError( "cant_prevent_ext_external_array_elements", @@ -5516,7 +5520,8 @@ Handle JSObject::Freeze(Handle object) { } // It's not possible to freeze objects with external array elements - if (object->HasExternalArrayElements()) { + if (object->HasExternalArrayElements() || + object->HasFixedTypedArrayElements()) { Handle error = isolate->factory()->NewTypeError( "cant_prevent_ext_external_array_elements", @@ -12435,7 +12440,9 @@ Handle JSObject::SetElement(Handle object, } // Don't allow element properties to be redefined for external arrays. - if (object->HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) { + if ((object->HasExternalArrayElements() || + object->HasFixedTypedArrayElements()) && + set_mode == DEFINE_PROPERTY) { Handle number = isolate->factory()->NewNumberFromUint(index); Handle args[] = { object, number }; Handle error = isolate->factory()->NewTypeError( @@ -14354,10 +14361,11 @@ Handle JSObject::PrepareElementsForSort(Handle object, object->ValidateElements(); object->set_map_and_elements(*new_map, *fast_elements); - } else if (object->HasExternalArrayElements()) { - // External arrays cannot have holes or undefined elements. + } else if (object->HasExternalArrayElements() || + object->HasFixedTypedArrayElements()) { + // Typed arrays cannot have holes or undefined elements. return handle(Smi::FromInt( - ExternalArray::cast(object->elements())->length()), isolate); + FixedArrayBase::cast(object->elements())->length()), isolate); } else if (!object->HasFastDoubleElements()) { EnsureWritableFastElements(object); } @@ -14458,12 +14466,14 @@ ExternalArrayType JSTypedArray::type() { switch (elements()->map()->instance_type()) { #define INSTANCE_TYPE_TO_ARRAY_TYPE(Type, type, TYPE, ctype, size) \ case EXTERNAL_##TYPE##_ARRAY_TYPE: \ + case FIXED_##TYPE##_ARRAY_TYPE: \ return kExternal##Type##Array; TYPED_ARRAYS(INSTANCE_TYPE_TO_ARRAY_TYPE) #undef INSTANCE_TYPE_TO_ARRAY_TYPE default: + UNREACHABLE(); return static_cast(-1); } } @@ -16302,6 +16312,65 @@ void JSTypedArray::Neuter() { } +static ElementsKind FixedToExternalElementsKind(ElementsKind elements_kind) { + switch (elements_kind) { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: return EXTERNAL_##TYPE##_ELEMENTS; + + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + + default: + UNREACHABLE(); + return FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND; + } +} + + +Handle JSTypedArray::MaterializeArrayBuffer( + Handle typed_array) { + + Handle map(typed_array->map()); + Isolate* isolate = typed_array->GetIsolate(); + + ASSERT(IsFixedTypedArrayElementsKind(map->elements_kind())); + + Handle buffer = isolate->factory()->NewJSArrayBuffer(); + Handle fixed_typed_array( + FixedTypedArrayBase::cast(typed_array->elements())); + Runtime::SetupArrayBufferAllocatingData(isolate, buffer, + fixed_typed_array->DataSize(), false); + memcpy(buffer->backing_store(), + fixed_typed_array->DataPtr(), + fixed_typed_array->DataSize()); + Handle new_elements = + isolate->factory()->NewExternalArray( + fixed_typed_array->length(), typed_array->type(), + static_cast(buffer->backing_store())); + Handle new_map = JSObject::GetElementsTransitionMap( + typed_array, + FixedToExternalElementsKind(map->elements_kind())); + + buffer->set_weak_first_view(*typed_array); + ASSERT(typed_array->weak_next() == isolate->heap()->undefined_value()); + typed_array->set_buffer(*buffer); + typed_array->set_map_and_elements(*new_map, *new_elements); + + return buffer; +} + + +Handle JSTypedArray::GetBuffer() { + Handle result(buffer(), GetIsolate()); + if (*result != Smi::FromInt(0)) { + ASSERT(IsExternalArrayElementsKind(map()->elements_kind())); + return Handle::cast(result); + } + Handle self(this); + return MaterializeArrayBuffer(self); +} + + HeapType* PropertyCell::type() { return static_cast(type_raw()); } diff --git a/src/objects.h b/src/objects.h index edb7903..35fed0a 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2217,6 +2217,17 @@ class JSObject: public JSReceiver { inline bool HasFixedTypedArrayElements(); + inline bool HasFixedUint8ClampedElements(); + inline bool HasFixedArrayElements(); + inline bool HasFixedInt8Elements(); + inline bool HasFixedUint8Elements(); + inline bool HasFixedInt16Elements(); + inline bool HasFixedUint16Elements(); + inline bool HasFixedInt32Elements(); + inline bool HasFixedUint32Elements(); + inline bool HasFixedFloat32Elements(); + inline bool HasFixedFloat64Elements(); + bool HasFastArgumentsElements(); bool HasDictionaryArgumentsElements(); inline SeededNumberDictionary* element_dictionary(); // Gets slow elements. @@ -4956,6 +4967,11 @@ class FixedTypedArrayBase: public FixedArrayBase { inline int size(); + // Use with care: returns raw pointer into heap. + inline void* DataPtr(); + + inline int DataSize(); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(FixedTypedArrayBase); }; @@ -4982,6 +4998,9 @@ class FixedTypedArray: public FixedTypedArrayBase { MUST_USE_RESULT inline MaybeObject* get(int index); inline void set(int index, ElementType value); + static inline ElementType from_int(int value); + static inline ElementType from_double(double value); + // This accessor applies the correct conversion from Smi, HeapNumber // and undefined. MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value); @@ -5004,7 +5023,7 @@ class FixedTypedArray: public FixedTypedArrayBase { static const InstanceType kInstanceType = FIXED_##TYPE##_ARRAY_TYPE; \ static const char* Designator() { return #type " array"; } \ static inline MaybeObject* ToObject(Heap* heap, elementType scalar); \ - static elementType defaultValue() { return 0; } \ + static inline elementType defaultValue(); \ }; \ \ typedef FixedTypedArray Fixed##Type##Array; @@ -6224,9 +6243,11 @@ class Map: public HeapObject { Descriptor* descriptor, int index, TransitionFlag flag); - static Handle AsElementsKind(Handle map, ElementsKind kind); + MUST_USE_RESULT MaybeObject* AsElementsKind(ElementsKind kind); + static Handle AsElementsKind(Handle map, ElementsKind kind); + MUST_USE_RESULT MaybeObject* CopyAsElementsKind(ElementsKind kind, TransitionFlag flag); @@ -9916,6 +9937,8 @@ class JSTypedArray: public JSArrayBufferView { ExternalArrayType type(); size_t element_size(); + Handle GetBuffer(); + // Dispatched behavior. DECLARE_PRINTER(JSTypedArray) DECLARE_VERIFIER(JSTypedArray) @@ -9927,6 +9950,9 @@ class JSTypedArray: public JSArrayBufferView { kSize + v8::ArrayBufferView::kInternalFieldCount * kPointerSize; private: + static Handle MaterializeArrayBuffer( + Handle typed_array); + DISALLOW_IMPLICIT_CONSTRUCTORS(JSTypedArray); }; diff --git a/src/runtime.cc b/src/runtime.cc index cfa427c..b7fcb2d 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -926,11 +926,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferNeuter) { void Runtime::ArrayIdToTypeAndSize( - int arrayId, ExternalArrayType* array_type, size_t* element_size) { + int arrayId, + ExternalArrayType* array_type, + ElementsKind* external_elements_kind, + ElementsKind* fixed_elements_kind, + size_t* element_size) { switch (arrayId) { #define ARRAY_ID_CASE(Type, type, TYPE, ctype, size) \ case ARRAY_ID_##TYPE: \ *array_type = kExternal##Type##Array; \ + *external_elements_kind = EXTERNAL_##TYPE##_ELEMENTS; \ + *fixed_elements_kind = TYPE##_ELEMENTS; \ *element_size = size; \ break; @@ -948,7 +954,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { ASSERT(args.length() == 5); CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); CONVERT_SMI_ARG_CHECKED(arrayId, 1); - CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 2); + CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2); CONVERT_ARG_HANDLE_CHECKED(Object, byte_offset_object, 3); CONVERT_ARG_HANDLE_CHECKED(Object, byte_length_object, 4); @@ -960,18 +966,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization. size_t element_size = 1; // Bogus initialization. - Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &element_size); + ElementsKind external_elements_kind = EXTERNAL_INT8_ELEMENTS; + ElementsKind fixed_elements_kind = INT8_ELEMENTS; + Runtime::ArrayIdToTypeAndSize(arrayId, + &array_type, + &external_elements_kind, + &fixed_elements_kind, + &element_size); - holder->set_buffer(*buffer); holder->set_byte_offset(*byte_offset_object); holder->set_byte_length(*byte_length_object); size_t byte_offset = NumberToSize(isolate, *byte_offset_object); size_t byte_length = NumberToSize(isolate, *byte_length_object); - size_t array_buffer_byte_length = - NumberToSize(isolate, buffer->byte_length()); - CHECK(byte_offset <= array_buffer_byte_length); - CHECK(array_buffer_byte_length - byte_offset >= byte_length); CHECK_EQ(0, static_cast(byte_length % element_size)); size_t length = byte_length / element_size; @@ -984,14 +991,34 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { Handle length_obj = isolate->factory()->NewNumberFromSize(length); holder->set_length(*length_obj); - holder->set_weak_next(buffer->weak_first_view()); - buffer->set_weak_first_view(*holder); - - Handle elements = - isolate->factory()->NewExternalArray( - static_cast(length), array_type, - static_cast(buffer->backing_store()) + byte_offset); - holder->set_elements(*elements); + if (!maybe_buffer->IsNull()) { + Handle buffer(JSArrayBuffer::cast(*maybe_buffer)); + + size_t array_buffer_byte_length = + NumberToSize(isolate, buffer->byte_length()); + CHECK(byte_offset <= array_buffer_byte_length); + CHECK(array_buffer_byte_length - byte_offset >= byte_length); + + holder->set_buffer(*buffer); + holder->set_weak_next(buffer->weak_first_view()); + buffer->set_weak_first_view(*holder); + + Handle elements = + isolate->factory()->NewExternalArray( + static_cast(length), array_type, + static_cast(buffer->backing_store()) + byte_offset); + Handle map = + JSObject::GetElementsTransitionMap(holder, external_elements_kind); + holder->set_map_and_elements(*map, *elements); + ASSERT(IsExternalArrayElementsKind(holder->map()->elements_kind())); + } else { + holder->set_buffer(Smi::FromInt(0)); + holder->set_weak_next(isolate->heap()->undefined_value()); + Handle elements = + isolate->factory()->NewFixedTypedArray( + static_cast(length), array_type); + holder->set_elements(*elements); + } return isolate->heap()->undefined_value(); } @@ -1017,7 +1044,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitializeFromArrayLike) { ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization. size_t element_size = 1; // Bogus initialization. - Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &element_size); + ElementsKind external_elements_kind; + ElementsKind fixed_elements_kind; + Runtime::ArrayIdToTypeAndSize(arrayId, + &array_type, + &external_elements_kind, + &fixed_elements_kind, + &element_size); Handle buffer = isolate->factory()->NewJSArrayBuffer(); if (source->IsJSTypedArray() && @@ -1070,7 +1103,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitializeFromArrayLike) { isolate->factory()->NewExternalArray( static_cast(length), array_type, static_cast(buffer->backing_store())); - holder->set_elements(*elements); + Handle map = JSObject::GetElementsTransitionMap( + holder, external_elements_kind); + holder->set_map_and_elements(*map, *elements); if (source->IsJSTypedArray()) { Handle typed_array(JSTypedArray::cast(*source)); @@ -1078,7 +1113,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitializeFromArrayLike) { if (typed_array->type() == holder->type()) { uint8_t* backing_store = static_cast( - JSArrayBuffer::cast(typed_array->buffer())->backing_store()); + typed_array->GetBuffer()->backing_store()); size_t source_byte_offset = NumberToSize(isolate, typed_array->byte_offset()); memcpy( @@ -1107,13 +1142,24 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitializeFromArrayLike) { return typed_array->accessor(); \ } -TYPED_ARRAY_GETTER(Buffer, buffer) TYPED_ARRAY_GETTER(ByteLength, byte_length) TYPED_ARRAY_GETTER(ByteOffset, byte_offset) TYPED_ARRAY_GETTER(Length, length) #undef TYPED_ARRAY_GETTER +RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayGetBuffer) { + HandleScope scope(isolate); + ASSERT(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(Object, holder, 0); + if (!holder->IsJSTypedArray()) + return isolate->Throw(*isolate->factory()->NewTypeError( + "not_typed_array", HandleVector(NULL, 0))); + Handle typed_array(JSTypedArray::cast(*holder)); + return *typed_array->GetBuffer(); +} + + // Return codes for Runtime_TypedArraySetFastCases. // Should be synchronized with typedarray.js natives. enum TypedArraySetResultCodes { @@ -1159,10 +1205,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { size_t source_offset = NumberToSize(isolate, source->byte_offset()); uint8_t* target_base = static_cast( - JSArrayBuffer::cast(target->buffer())->backing_store()) + target_offset; + target->GetBuffer()->backing_store()) + target_offset; uint8_t* source_base = static_cast( - JSArrayBuffer::cast(source->buffer())->backing_store()) + source_offset; + source->GetBuffer()->backing_store()) + source_offset; // Typed arrays of the same type: use memmove. if (target->type() == source->type()) { @@ -1178,8 +1224,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { target_base + target_byte_length > source_base)) { // We do not support overlapping ArrayBuffers ASSERT( - JSArrayBuffer::cast(target->buffer())->backing_store() == - JSArrayBuffer::cast(source->buffer())->backing_store()); + target->GetBuffer()->backing_store() == + source->GetBuffer()->backing_store()); return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING); } else { // Non-overlapping typed arrays return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING); @@ -1187,6 +1233,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayMaxSizeInHeap) { + ASSERT_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap); + return Smi::FromInt(FLAG_typed_array_max_size_in_heap); +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewInitialize) { HandleScope scope(isolate); ASSERT(args.length() == 4); @@ -14739,6 +14791,17 @@ TYPED_ARRAYS(TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION) #undef TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION +#define FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION(Type, type, TYPE, ctype, s) \ + RUNTIME_FUNCTION(MaybeObject*, Runtime_HasFixed##Type##Elements) { \ + CONVERT_ARG_CHECKED(JSObject, obj, 0); \ + return isolate->heap()->ToBoolean(obj->HasFixed##Type##Elements()); \ + } + +TYPED_ARRAYS(FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION) + +#undef FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION + + RUNTIME_FUNCTION(MaybeObject*, Runtime_HaveSameMap) { SealHandleScope shs(isolate); ASSERT(args.length() == 2); diff --git a/src/runtime.h b/src/runtime.h index 7c74999..28dbe30 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -468,6 +468,15 @@ namespace internal { F(HasExternalUint32Elements, 1, 1) \ F(HasExternalFloat32Elements, 1, 1) \ F(HasExternalFloat64Elements, 1, 1) \ + F(HasFixedUint8ClampedElements, 1, 1) \ + F(HasFixedInt8Elements, 1, 1) \ + F(HasFixedUint8Elements, 1, 1) \ + F(HasFixedInt16Elements, 1, 1) \ + F(HasFixedUint16Elements, 1, 1) \ + F(HasFixedInt32Elements, 1, 1) \ + F(HasFixedUint32Elements, 1, 1) \ + F(HasFixedFloat32Elements, 1, 1) \ + F(HasFixedFloat64Elements, 1, 1) \ F(HasFastProperties, 1, 1) \ F(TransitionElementsKind, 2, 1) \ F(HaveSameMap, 2, 1) \ @@ -676,7 +685,8 @@ namespace internal { F(ConstructDouble, 2, 1) \ F(TypedArrayInitialize, 5, 1) \ F(DataViewInitialize, 4, 1) \ - F(MaxSmi, 0, 1) + F(MaxSmi, 0, 1) \ + F(TypedArrayMaxSizeInHeap, 0, 1) //--------------------------------------------------------------------------- @@ -868,7 +878,10 @@ class Runtime : public AllStatic { }; static void ArrayIdToTypeAndSize(int array_id, - ExternalArrayType *type, size_t *element_size); + ExternalArrayType *type, + ElementsKind* external_elements_kind, + ElementsKind* fixed_elements_kind, + size_t *element_size); // Helper functions used stubs. static void PerformGC(Object* result, Isolate* isolate); diff --git a/src/typedarray.js b/src/typedarray.js index d13ee61..109d627 100644 --- a/src/typedarray.js +++ b/src/typedarray.js @@ -100,8 +100,12 @@ macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE) throw MakeRangeError("invalid_typed_array_length"); } var byteLength = l * ELEMENT_SIZE; - var buffer = new $ArrayBuffer(byteLength); - %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength); + if (byteLength > %_TypedArrayMaxSizeInHeap()) { + var buffer = new $ArrayBuffer(byteLength); + %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength); + } else { + %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength); + } } function NAMEConstructByArrayLike(obj, arrayLike) { diff --git a/test/mjsunit/elements-kind.js b/test/mjsunit/elements-kind.js index e2bbc31..3aa513a 100644 --- a/test/mjsunit/elements-kind.js +++ b/test/mjsunit/elements-kind.js @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc --nostress-opt +// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc --nostress-opt --typed-array-max_size_in-heap=2048 // Test element kind of objects. // Since --smi-only-arrays affects builtins, its default setting at compile @@ -43,19 +43,28 @@ if (support_smi_only_arrays) { } var elements_kind = { - fast_smi_only : 'fast smi only elements', - fast : 'fast elements', - fast_double : 'fast double elements', - dictionary : 'dictionary elements', - external_byte : 'external byte elements', - external_unsigned_byte : 'external unsigned byte elements', - external_short : 'external short elements', - external_unsigned_short : 'external unsigned short elements', - external_int : 'external int elements', - external_unsigned_int : 'external unsigned int elements', - external_float : 'external float elements', - external_double : 'external double elements', - external_pixel : 'external pixel elements' + fast_smi_only : 'fast smi only elements', + fast : 'fast elements', + fast_double : 'fast double elements', + dictionary : 'dictionary elements', + external_int32 : 'external int8 elements', + external_uint8 : 'external uint8 elements', + external_int16 : 'external int16 elements', + external_uint16 : 'external uint16 elements', + external_int32 : 'external int32 elements', + external_uint32 : 'external uint32 elements', + external_float32 : 'external float32 elements', + external_float64 : 'external float64 elements', + external_uint8_clamped : 'external uint8_clamped elements', + fixed_int32 : 'fixed int8 elements', + fixed_uint8 : 'fixed uint8 elements', + fixed_int16 : 'fixed int16 elements', + fixed_uint16 : 'fixed uint16 elements', + fixed_int32 : 'fixed int32 elements', + fixed_uint32 : 'fixed uint32 elements', + fixed_float32 : 'fixed float32 elements', + fixed_float64 : 'fixed float64 elements', + fixed_uint8_clamped : 'fixed uint8_clamped elements' } function getKind(obj) { @@ -63,34 +72,61 @@ function getKind(obj) { if (%HasFastObjectElements(obj)) return elements_kind.fast; if (%HasFastDoubleElements(obj)) return elements_kind.fast_double; if (%HasDictionaryElements(obj)) return elements_kind.dictionary; + // Every external kind is also an external array. - assertTrue(%HasExternalArrayElements(obj)); if (%HasExternalInt8Elements(obj)) { - return elements_kind.external_byte; + return elements_kind.external_int8; } if (%HasExternalUint8Elements(obj)) { - return elements_kind.external_unsigned_byte; + return elements_kind.external_uint8; } if (%HasExternalInt16Elements(obj)) { - return elements_kind.external_short; + return elements_kind.external_int16; } if (%HasExternalUint16Elements(obj)) { - return elements_kind.external_unsigned_short; + return elements_kind.external_uint16; } if (%HasExternalInt32Elements(obj)) { - return elements_kind.external_int; + return elements_kind.external_int32; } if (%HasExternalUint32Elements(obj)) { - return elements_kind.external_unsigned_int; + return elements_kind.external_uint32; } if (%HasExternalFloat32Elements(obj)) { - return elements_kind.external_float; + return elements_kind.external_float32; } if (%HasExternalFloat64Elements(obj)) { - return elements_kind.external_double; + return elements_kind.external_float64; } if (%HasExternalUint8ClampedElements(obj)) { - return elements_kind.external_pixel; + return elements_kind.external_uint8_clamped; + } + if (%HasFixedInt8Elements(obj)) { + return elements_kind.fixed_int8; + } + if (%HasFixedUint8Elements(obj)) { + return elements_kind.fixed_uint8; + } + if (%HasFixedInt16Elements(obj)) { + return elements_kind.fixed_int16; + } + if (%HasFixedUint16Elements(obj)) { + return elements_kind.fixed_uint16; + } + if (%HasFixedInt32Elements(obj)) { + return elements_kind.fixed_int32; + } + if (%HasFixedUint32Elements(obj)) { + return elements_kind.fixed_uint32; + } + if (%HasFixedFloat32Elements(obj)) { + return elements_kind.fixed_float32; + } + if (%HasFixedFloat64Elements(obj)) { + return elements_kind.fixed_float64; + } + if (%HasFixedUint8ClampedElements(obj)) { + return elements_kind.fixed_uint8_clamped; } } @@ -136,15 +172,26 @@ function test_wrapper() { for (var i = 0; i < 0xDECAF; i++) fast_double_array[i] = i / 2; assertKind(elements_kind.fast_double, fast_double_array); - assertKind(elements_kind.external_byte, new Int8Array(9001)); - assertKind(elements_kind.external_unsigned_byte, new Uint8Array(007)); - assertKind(elements_kind.external_short, new Int16Array(666)); - assertKind(elements_kind.external_unsigned_short, new Uint16Array(42)); - assertKind(elements_kind.external_int, new Int32Array(0xF)); - assertKind(elements_kind.external_unsigned_int, new Uint32Array(23)); - assertKind(elements_kind.external_float, new Float32Array(7)); - assertKind(elements_kind.external_double, new Float64Array(0)); - assertKind(elements_kind.external_pixel, new Uint8ClampedArray(512)); + assertKind(elements_kind.fixed_int8, new Int8Array(007)); + assertKind(elements_kind.fixed_uint8, new Uint8Array(007)); + assertKind(elements_kind.fixed_int16, new Int16Array(666)); + assertKind(elements_kind.fixed_uint16, new Uint16Array(42)); + assertKind(elements_kind.fixed_int32, new Int32Array(0xF)); + assertKind(elements_kind.fixed_uint32, new Uint32Array(23)); + assertKind(elements_kind.fixed_float32, new Float32Array(7)); + assertKind(elements_kind.fixed_float64, new Float64Array(0)); + assertKind(elements_kind.fixed_uint8_clamped, new Uint8ClampedArray(512)); + + var ab = new ArrayBuffer(128); + assertKind(elements_kind.external_int8, new Int8Array(ab)); + assertKind(elements_kind.external_uint8, new Uint8Array(ab)); + assertKind(elements_kind.external_int16, new Int16Array(ab)); + assertKind(elements_kind.external_uint16, new Uint16Array(ab)); + assertKind(elements_kind.external_int32, new Int32Array(ab)); + assertKind(elements_kind.external_uint32, new Uint32Array(ab)); + assertKind(elements_kind.external_float32, new Float32Array(ab)); + assertKind(elements_kind.external_float64, new Float64Array(ab)); + assertKind(elements_kind.external_uint8_clamped, new Uint8ClampedArray(ab)); // Crankshaft support for smi-only array elements. function monomorphic(array) { diff --git a/test/mjsunit/external-array.js b/test/mjsunit/external-array.js index ab5435e..108b9f2 100644 --- a/test/mjsunit/external-array.js +++ b/test/mjsunit/external-array.js @@ -530,6 +530,7 @@ assertThrows(function() { ArrayBuffer.apply(null, [1000]); }, TypeError); assertThrows(function() { Float32Array.apply(null, [b, 128, 1]); }, TypeError); // Test array.set in different combinations. +var b = new ArrayBuffer(4) function assertArrayPrefix(expected, array) { for (var i = 0; i < expected.length; ++i) { -- 2.7.4