From 46e6297e3e41b8d3c72d9a10df98e26fb7869cd0 Mon Sep 17 00:00:00 2001 From: "kbr@chromium.org" Date: Tue, 20 Oct 2009 15:26:17 +0000 Subject: [PATCH] Added infrastructure for optimizing new CanvasArray types in WebGL specification under development. The optimizations are patterned after those previously done for CanvasPixelArray. This CL adds all of the necessary framework but continues to use the generic KeyedLoadIC and KeyedStoreIC code, to create a baseline for benchmarking purposes. The next CL will add the optimized ICs to ic-ia32.cc and ic-x64.cc. These new CanvasArray types have different semantics than CanvasPixelArray; out-of-range values are clamped via C cast semantics, which is cheaper than the clamping behavior specified by CanvasPixelArray. Out-of-range indices raise exceptions instead of being silently ignored. As part of this work, pulled FloatingPointHelper::AllocateHeapNumber up to MacroAssembler on ia32 and x64 platforms. Slightly refactored KeyedLoadIC and KeyedStoreIC. Fixed encoding for fistp_d on x64 and added a few more instructions that are needed for the new ICs. The test cases in test-api.cc have been verified by hand to exercise all of the generated code paths in the forthcoming specialized ICs. Review URL: http://codereview.chromium.org/293023 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3096 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 21 +++ src/api.cc | 24 +++ src/arm/ic-arm.cc | 14 ++ src/builtins.cc | 76 +++++++++ src/builtins.h | 90 +++++----- src/factory.cc | 20 +++ src/factory.h | 11 +- src/handles.cc | 2 +- src/heap.cc | 89 ++++++++++ src/heap.h | 20 +++ src/ia32/codegen-ia32.cc | 52 +----- src/ia32/ic-ia32.cc | 24 ++- src/ia32/macro-assembler-ia32.cc | 18 ++ src/ia32/macro-assembler-ia32.h | 9 + src/ic.cc | 69 +++++++- src/ic.h | 16 ++ src/messages.js | 1 + src/objects-debug.cc | 132 ++++++++++++++ src/objects-inl.h | 248 ++++++++++++++++++++++++++- src/objects.cc | 361 +++++++++++++++++++++++++++++++++++++-- src/objects.h | 244 +++++++++++++++++++++++++- src/runtime.cc | 109 +++++++++++- src/v8-counters.h | 1 + src/x64/assembler-x64.cc | 41 ++++- src/x64/assembler-x64.h | 7 + src/x64/codegen-x64.cc | 42 +---- src/x64/ic-x64.cc | 24 ++- src/x64/macro-assembler-x64.cc | 32 ++++ src/x64/macro-assembler-x64.h | 14 ++ test/cctest/test-api.cc | 321 ++++++++++++++++++++++++++++++++++ 30 files changed, 1971 insertions(+), 161 deletions(-) diff --git a/include/v8.h b/include/v8.h index 1a34989..b2a3fb7 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1126,6 +1126,16 @@ enum PropertyAttribute { DontDelete = 1 << 2 }; +enum ExternalArrayType { + kExternalByteArray = 1, + kExternalUnsignedByteArray, + kExternalShortArray, + kExternalUnsignedShortArray, + kExternalIntArray, + kExternalUnsignedIntArray, + kExternalFloatArray +}; + /** * A JavaScript object (ECMA-262, 4.3.3) */ @@ -1278,6 +1288,17 @@ class V8EXPORT Object : public Value { */ void SetIndexedPropertiesToPixelData(uint8_t* data, int length); + /** + * Set the backing store of the indexed properties to be managed by the + * embedding layer. Access to the indexed properties will follow the rules + * spelled out for the CanvasArray subtypes in the WebGL specification. + * Note: The embedding program still owns the data and needs to ensure that + * the backing store is preserved while V8 has a reference. + */ + void SetIndexedPropertiesToExternalArrayData(void* data, + ExternalArrayType array_type, + int number_of_elements); + static Local New(); static inline Object* Cast(Value* obj); private: diff --git a/src/api.cc b/src/api.cc index 7f0a673..db49b22 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2306,6 +2306,30 @@ void v8::Object::SetIndexedPropertiesToPixelData(uint8_t* data, int length) { } +void v8::Object::SetIndexedPropertiesToExternalArrayData( + void* data, + ExternalArrayType array_type, + int length) { + ON_BAILOUT("v8::SetIndexedPropertiesToExternalArrayData()", return); + ENTER_V8; + HandleScope scope; + if (!ApiCheck(length <= i::ExternalArray::kMaxLength, + "v8::Object::SetIndexedPropertiesToExternalArrayData()", + "length exceeds max acceptable value")) { + return; + } + i::Handle self = Utils::OpenHandle(this); + if (!ApiCheck(!self->IsJSArray(), + "v8::Object::SetIndexedPropertiesToExternalArrayData()", + "JSArray is not supported")) { + return; + } + i::Handle array = + i::Factory::NewExternalArray(length, array_type, data); + self->set_elements(*array); +} + + Local Function::NewInstance() const { return NewInstance(0, NULL); } diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index d230b45..ba83645 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -615,6 +615,13 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { } +void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type) { + // TODO(476): port specialized code. + GenerateGeneric(masm); +} + + void KeyedStoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) { // ---------- S t a t e -------------- @@ -748,6 +755,13 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { } +void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type) { + // TODO(476): port specialized code. + GenerateGeneric(masm); +} + + void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) { // ---------- S t a t e -------------- // -- r0 : value diff --git a/src/builtins.cc b/src/builtins.cc index afb5427..fa1b34e 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -538,6 +538,44 @@ static void Generate_KeyedLoadIC_Generic(MacroAssembler* masm) { } +static void Generate_KeyedLoadIC_ExternalByteArray(MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalByteArray); +} + + +static void Generate_KeyedLoadIC_ExternalUnsignedByteArray( + MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalUnsignedByteArray); +} + + +static void Generate_KeyedLoadIC_ExternalShortArray(MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalShortArray); +} + + +static void Generate_KeyedLoadIC_ExternalUnsignedShortArray( + MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalUnsignedShortArray); +} + + +static void Generate_KeyedLoadIC_ExternalIntArray(MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalIntArray); +} + + +static void Generate_KeyedLoadIC_ExternalUnsignedIntArray( + MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalUnsignedIntArray); +} + + +static void Generate_KeyedLoadIC_ExternalFloatArray(MacroAssembler* masm) { + KeyedLoadIC::GenerateExternalArray(masm, kExternalFloatArray); +} + + static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) { KeyedLoadIC::GeneratePreMonomorphic(masm); } @@ -567,6 +605,44 @@ static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) { } +static void Generate_KeyedStoreIC_ExternalByteArray(MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalByteArray); +} + + +static void Generate_KeyedStoreIC_ExternalUnsignedByteArray( + MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalUnsignedByteArray); +} + + +static void Generate_KeyedStoreIC_ExternalShortArray(MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalShortArray); +} + + +static void Generate_KeyedStoreIC_ExternalUnsignedShortArray( + MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalUnsignedShortArray); +} + + +static void Generate_KeyedStoreIC_ExternalIntArray(MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalIntArray); +} + + +static void Generate_KeyedStoreIC_ExternalUnsignedIntArray( + MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalUnsignedIntArray); +} + + +static void Generate_KeyedStoreIC_ExternalFloatArray(MacroAssembler* masm) { + KeyedStoreIC::GenerateExternalArray(masm, kExternalFloatArray); +} + + static void Generate_KeyedStoreIC_ExtendStorage(MacroAssembler* masm) { KeyedStoreIC::GenerateExtendStorage(masm); } diff --git a/src/builtins.h b/src/builtins.h index 141d5b7..bc32c49 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -48,44 +48,58 @@ namespace internal { // Define list of builtins implemented in assembly. -#define BUILTIN_LIST_A(V) \ - V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED) \ - V(JSConstructCall, BUILTIN, UNINITIALIZED) \ - V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED) \ - V(JSEntryTrampoline, BUILTIN, UNINITIALIZED) \ - V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED) \ - \ - V(LoadIC_Miss, BUILTIN, UNINITIALIZED) \ - V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED) \ - V(StoreIC_Miss, BUILTIN, UNINITIALIZED) \ - V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED) \ - \ - V(StoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \ - V(KeyedStoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \ - \ - V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED) \ - V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC) \ - V(LoadIC_Normal, LOAD_IC, MONOMORPHIC) \ - V(LoadIC_ArrayLength, LOAD_IC, MONOMORPHIC) \ - V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC) \ - V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC) \ - V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC) \ - \ - V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED) \ - V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC) \ - V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC) \ - \ - V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \ - V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \ - \ - V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \ - V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC) \ - \ - /* Uses KeyedLoadIC_Initialize; must be after in list. */ \ - V(FunctionCall, BUILTIN, UNINITIALIZED) \ - V(FunctionApply, BUILTIN, UNINITIALIZED) \ - \ - V(ArrayCode, BUILTIN, UNINITIALIZED) \ +#define BUILTIN_LIST_A(V) \ + V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED) \ + V(JSConstructCall, BUILTIN, UNINITIALIZED) \ + V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED) \ + V(JSEntryTrampoline, BUILTIN, UNINITIALIZED) \ + V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED) \ + \ + V(LoadIC_Miss, BUILTIN, UNINITIALIZED) \ + V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED) \ + V(StoreIC_Miss, BUILTIN, UNINITIALIZED) \ + V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED) \ + \ + V(StoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \ + V(KeyedStoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \ + \ + V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED) \ + V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC) \ + V(LoadIC_Normal, LOAD_IC, MONOMORPHIC) \ + V(LoadIC_ArrayLength, LOAD_IC, MONOMORPHIC) \ + V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC) \ + V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC) \ + V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC) \ + \ + V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED) \ + V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC) \ + V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalByteArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalUnsignedByteArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalShortArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalUnsignedShortArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalUnsignedIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_ExternalFloatArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + \ + V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \ + V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \ + \ + V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \ + V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalByteArray, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalUnsignedByteArray, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalShortArray, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalUnsignedShortArray, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalIntArray, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalUnsignedIntArray, KEYED_STORE_IC, MEGAMORPHIC) \ + V(KeyedStoreIC_ExternalFloatArray, KEYED_STORE_IC, MEGAMORPHIC) \ + \ + /* Uses KeyedLoadIC_Initialize; must be after in list. */ \ + V(FunctionCall, BUILTIN, UNINITIALIZED) \ + V(FunctionApply, BUILTIN, UNINITIALIZED) \ + \ + V(ArrayCode, BUILTIN, UNINITIALIZED) \ V(ArrayConstructCode, BUILTIN, UNINITIALIZED) #ifdef ENABLE_DEBUGGER_SUPPORT diff --git a/src/factory.cc b/src/factory.cc index 5251e34..3239f5e 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -222,6 +222,18 @@ Handle Factory::NewPixelArray(int length, } +Handle Factory::NewExternalArray(int length, + ExternalArrayType array_type, + void* external_pointer, + PretenureFlag pretenure) { + ASSERT(0 <= length); + CALL_HEAP_FUNCTION(Heap::AllocateExternalArray(length, + array_type, + external_pointer, + pretenure), ExternalArray); +} + + Handle Factory::NewMap(InstanceType type, int instance_size) { CALL_HEAP_FUNCTION(Heap::AllocateMap(type, instance_size), Map); } @@ -351,6 +363,14 @@ Handle Factory::NewRangeError(Handle message) { } +Handle Factory::NewIndexError(uint32_t index) { + Handle indexHandle = Handle(Heap::NumberFromUint32(index)); + return NewRangeError("invalid_array_index", + HandleVector(&indexHandle, + 1)); +} + + Handle Factory::NewSyntaxError(const char* type, Handle args) { return NewError("MakeSyntaxError", type, args); } diff --git a/src/factory.h b/src/factory.h index 7223f08..9f2652a 100644 --- a/src/factory.h +++ b/src/factory.h @@ -155,10 +155,17 @@ class Factory : public AllStatic { static Handle NewByteArray(int length, PretenureFlag pretenure = NOT_TENURED); - static Handle NewPixelArray(int length, + static Handle NewPixelArray( + int length, uint8_t* external_pointer, PretenureFlag pretenure = NOT_TENURED); + static Handle NewExternalArray( + int length, + ExternalArrayType array_type, + void* external_pointer, + PretenureFlag pretenure = NOT_TENURED); + static Handle NewMap(InstanceType type, int instance_size); static Handle NewFunctionPrototype(Handle function); @@ -245,6 +252,8 @@ class Factory : public AllStatic { Vector< Handle > args); static Handle NewRangeError(Handle message); + static Handle NewIndexError(uint32_t index); + static Handle NewSyntaxError(const char* type, Handle args); static Handle NewSyntaxError(Handle message); diff --git a/src/handles.cc b/src/handles.cc index b43ec53..b764334 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -345,7 +345,7 @@ Handle SubString(Handle str, int start, int end) { Handle SetElement(Handle object, uint32_t index, Handle value) { - if (object->HasPixelElements()) { + if (object->HasPixelElements() || object->HasExternalArrayElements()) { if (!value->IsSmi() && !value->IsHeapNumber() && !value->IsUndefined()) { bool has_exception; Handle number = Execution::ToNumber(value, &has_exception); diff --git a/src/heap.cc b/src/heap.cc index 29390cd..a6122af 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1218,6 +1218,41 @@ bool Heap::CreateInitialMaps() { if (obj->IsFailure()) return false; set_pixel_array_map(Map::cast(obj)); + obj = AllocateMap(EXTERNAL_BYTE_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_byte_array_map(Map::cast(obj)); + + obj = AllocateMap(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_unsigned_byte_array_map(Map::cast(obj)); + + obj = AllocateMap(EXTERNAL_SHORT_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_short_array_map(Map::cast(obj)); + + obj = AllocateMap(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_unsigned_short_array_map(Map::cast(obj)); + + obj = AllocateMap(EXTERNAL_INT_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_int_array_map(Map::cast(obj)); + + obj = AllocateMap(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_unsigned_int_array_map(Map::cast(obj)); + + obj = AllocateMap(EXTERNAL_FLOAT_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (obj->IsFailure()) return false; + set_external_float_array_map(Map::cast(obj)); + obj = AllocateMap(CODE_TYPE, Code::kHeaderSize); if (obj->IsFailure()) return false; set_code_map(Map::cast(obj)); @@ -1638,6 +1673,35 @@ Object* Heap::NumberToString(Object* number) { } +Map* Heap::MapForExternalArrayType(ExternalArrayType array_type) { + return Map::cast(roots_[RootIndexForExternalArrayType(array_type)]); +} + + +Heap::RootListIndex Heap::RootIndexForExternalArrayType( + ExternalArrayType array_type) { + switch (array_type) { + case kExternalByteArray: + return kExternalByteArrayMapRootIndex; + case kExternalUnsignedByteArray: + return kExternalUnsignedByteArrayMapRootIndex; + case kExternalShortArray: + return kExternalShortArrayMapRootIndex; + case kExternalUnsignedShortArray: + return kExternalUnsignedShortArrayMapRootIndex; + case kExternalIntArray: + return kExternalIntArrayMapRootIndex; + case kExternalUnsignedIntArray: + return kExternalUnsignedIntArrayMapRootIndex; + case kExternalFloatArray: + return kExternalFloatArrayMapRootIndex; + default: + UNREACHABLE(); + return kUndefinedValueRootIndex; + } +} + + Object* Heap::NewNumberFromDouble(double value, PretenureFlag pretenure) { return SmiOrNumberFromDouble(value, true /* number object must be new */, @@ -1963,6 +2027,31 @@ Object* Heap::AllocatePixelArray(int length, } +Object* Heap::AllocateExternalArray(int length, + ExternalArrayType array_type, + void* external_pointer, + PretenureFlag pretenure) { + AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE; + + // New space can't cope with forced allocation. + if (always_allocate()) space = OLD_DATA_SPACE; + + Object* result = AllocateRaw(ExternalArray::kAlignedSize, + space, + OLD_DATA_SPACE); + + if (result->IsFailure()) return result; + + reinterpret_cast(result)->set_map( + MapForExternalArrayType(array_type)); + reinterpret_cast(result)->set_length(length); + reinterpret_cast(result)->set_external_pointer( + external_pointer); + + return result; +} + + Object* Heap::CreateCode(const CodeDesc& desc, ZoneScopeInfo* sinfo, Code::Flags flags, diff --git a/src/heap.h b/src/heap.h index 738d2e5..4f263aa 100644 --- a/src/heap.h +++ b/src/heap.h @@ -111,6 +111,13 @@ namespace internal { V(Map, undetectable_long_ascii_string_map, UndetectableLongAsciiStringMap) \ V(Map, byte_array_map, ByteArrayMap) \ V(Map, pixel_array_map, PixelArrayMap) \ + V(Map, external_byte_array_map, ExternalByteArrayMap) \ + V(Map, external_unsigned_byte_array_map, ExternalUnsignedByteArrayMap) \ + V(Map, external_short_array_map, ExternalShortArrayMap) \ + V(Map, external_unsigned_short_array_map, ExternalUnsignedShortArrayMap) \ + V(Map, external_int_array_map, ExternalIntArrayMap) \ + V(Map, external_unsigned_int_array_map, ExternalUnsignedIntArrayMap) \ + V(Map, external_float_array_map, ExternalFloatArrayMap) \ V(Map, context_map, ContextMap) \ V(Map, catch_context_map, CatchContextMap) \ V(Map, code_map, CodeMap) \ @@ -452,6 +459,15 @@ class Heap : public AllStatic { uint8_t* external_pointer, PretenureFlag pretenure); + // Allocates an external array of the specified length and type. + // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation + // failed. + // Please note this does not perform a garbage collection. + static Object* AllocateExternalArray(int length, + ExternalArrayType array_type, + void* external_pointer, + PretenureFlag pretenure); + // Allocate a tenured JS global property cell. // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. @@ -884,6 +900,10 @@ class Heap : public AllStatic { static Object* NumberToString(Object* number); + static Map* MapForExternalArrayType(ExternalArrayType array_type); + static RootListIndex RootIndexForExternalArrayType( + ExternalArrayType array_type); + private: static int semispace_size_; static int initial_semispace_size_; diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index ce48e9d..e7b9648 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -773,13 +773,6 @@ class FloatingPointHelper : public AllStatic { // either operand is not a number. Operands are in edx and eax. // Leaves operands unchanged. static void LoadSse2Operands(MacroAssembler* masm, Label* not_numbers); - // Allocate a heap number in new space with undefined value. - // Returns tagged pointer in eax, or jumps to need_gc if new space is full. - static void AllocateHeapNumber(MacroAssembler* masm, - Label* need_gc, - Register scratch1, - Register scratch2, - Register result); }; @@ -5175,11 +5168,10 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* args) { Result scratch1 = allocator()->Allocate(); Result scratch2 = allocator()->Allocate(); Result heap_number = allocator()->Allocate(); - FloatingPointHelper::AllocateHeapNumber(masm_, - call_runtime.entry_label(), - scratch1.reg(), - scratch2.reg(), - heap_number.reg()); + __ AllocateHeapNumber(heap_number.reg(), + scratch1.reg(), + scratch2.reg(), + call_runtime.entry_label()); scratch1.Unuse(); scratch2.Unuse(); @@ -6836,11 +6828,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case NO_OVERWRITE: { // Allocate a heap number for the result. Keep eax and edx intact // for the possible runtime call. - FloatingPointHelper::AllocateHeapNumber(masm, - &call_runtime, - ecx, - no_reg, - ebx); + __ AllocateHeapNumber(ebx, ecx, no_reg, &call_runtime); // Now eax can be overwritten losing one of the arguments as we are // now done and will not need it any more. __ mov(eax, ebx); @@ -6868,11 +6856,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case NO_OVERWRITE: // Allocate a heap number for the result. Keep eax and edx intact // for the possible runtime call. - FloatingPointHelper::AllocateHeapNumber(masm, - &call_runtime, - ecx, - no_reg, - ebx); + __ AllocateHeapNumber(ebx, ecx, no_reg, &call_runtime); // Now eax can be overwritten losing one of the arguments as we are // now done and will not need it any more. __ mov(eax, ebx); @@ -6982,8 +6966,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ j(not_zero, &skip_allocation, not_taken); // Fall through! case NO_OVERWRITE: - FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime, - ecx, edx, eax); + __ AllocateHeapNumber(eax, ecx, edx, &call_runtime); __ bind(&skip_allocation); break; default: UNREACHABLE(); @@ -7133,25 +7116,6 @@ void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) { } -void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm, - Label* need_gc, - Register scratch1, - Register scratch2, - Register result) { - // Allocate heap number in new space. - __ AllocateInNewSpace(HeapNumber::kSize, - result, - scratch1, - scratch2, - need_gc, - TAG_OBJECT); - - // Set the map. - __ mov(FieldOperand(result, HeapObject::kMapOffset), - Immediate(Factory::heap_number_map())); -} - - void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, Register number) { Label load_smi, done; @@ -7308,7 +7272,7 @@ void UnarySubStub::Generate(MacroAssembler* masm) { } else { __ mov(edx, Operand(eax)); // edx: operand - FloatingPointHelper::AllocateHeapNumber(masm, &undo, ebx, ecx, eax); + __ AllocateHeapNumber(eax, ebx, ecx, &undo); // eax: allocated 'empty' number __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset)); __ xor_(ecx, HeapNumber::kSignMask); // Flip sign. diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index af05680..92632d7 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -301,7 +301,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: Load name and receiver from stack and jump to runtime. __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_generic_slow, 1); - KeyedLoadIC::Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); + Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); __ bind(&check_string); // The key is not a smi. @@ -342,6 +342,12 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { } +void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type) { + GenerateGeneric(masm); +} + + void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : value @@ -395,15 +401,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // ebx: index (as a smi) __ j(below, &fast, taken); - // Slow case: Push extra copies of the arguments (3). + // Slow case: call runtime. __ bind(&slow); - __ pop(ecx); - __ push(Operand(esp, 1 * kPointerSize)); - __ push(Operand(esp, 1 * kPointerSize)); - __ push(eax); - __ push(ecx); - // Do tail-call to runtime routine. - __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); + Generate(masm, ExternalReference(Runtime::kSetProperty)); // Check whether the elements is a pixel array. // eax: value @@ -485,6 +485,12 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { } +void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type) { + GenerateGeneric(masm); +} + + // Defined in ic.cc. Object* CallIC_Miss(Arguments args); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index a3b2149..af21c74 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -767,6 +767,24 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) { } +void MacroAssembler::AllocateHeapNumber(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(HeapNumber::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::heap_number_map())); +} + + void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, Register result, Register op, diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index ed72c96..a0a2428 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -206,6 +206,15 @@ class MacroAssembler: public Assembler { // un-done. void UndoAllocationInNewSpace(Register object); + // Allocate a heap number in new space with undefined value. The + // register scratch2 can be passed as no_reg; the others must be + // valid registers. Returns tagged pointer in result register, or + // jumps to gc_required if new space is full. + void AllocateHeapNumber(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + // --------------------------------------------------------------------------- // Support functions. diff --git a/src/ic.cc b/src/ic.cc index 264b99c..c12dba7 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -265,6 +265,55 @@ void KeyedStoreIC::Clear(Address address, Code* target) { } +Code* KeyedLoadIC::external_array_stub(JSObject::ElementsKind elements_kind) { + switch (elements_kind) { + case JSObject::EXTERNAL_BYTE_ELEMENTS: + return Builtins::builtin(Builtins::KeyedLoadIC_ExternalByteArray); + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedByteArray); + case JSObject::EXTERNAL_SHORT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedLoadIC_ExternalShortArray); + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + return Builtins::builtin( + Builtins::KeyedLoadIC_ExternalUnsignedShortArray); + case JSObject::EXTERNAL_INT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedLoadIC_ExternalIntArray); + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedIntArray); + case JSObject::EXTERNAL_FLOAT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedLoadIC_ExternalFloatArray); + default: + UNREACHABLE(); + return NULL; + } +} + + +Code* KeyedStoreIC::external_array_stub(JSObject::ElementsKind elements_kind) { + switch (elements_kind) { + case JSObject::EXTERNAL_BYTE_ELEMENTS: + return Builtins::builtin(Builtins::KeyedStoreIC_ExternalByteArray); + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + return Builtins::builtin( + Builtins::KeyedStoreIC_ExternalUnsignedByteArray); + case JSObject::EXTERNAL_SHORT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedStoreIC_ExternalShortArray); + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + return Builtins::builtin( + Builtins::KeyedStoreIC_ExternalUnsignedShortArray); + case JSObject::EXTERNAL_INT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedStoreIC_ExternalIntArray); + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedStoreIC_ExternalUnsignedIntArray); + case JSObject::EXTERNAL_FLOAT_ELEMENTS: + return Builtins::builtin(Builtins::KeyedStoreIC_ExternalFloatArray); + default: + UNREACHABLE(); + return NULL; + } +} + + static bool HasInterceptorGetter(JSObject* object) { return !object->GetNamedInterceptor()->getter()->IsUndefined(); } @@ -823,7 +872,14 @@ Object* KeyedLoadIC::Load(State state, bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded(); if (use_ic) { - set_target(generic_stub()); + Code* stub = generic_stub(); + if (object->IsJSObject()) { + Handle receiver = Handle::cast(object); + if (receiver->HasExternalArrayElements()) { + stub = external_array_stub(receiver->GetElementsKind()); + } + } + set_target(stub); // For JSObjects that are not value wrappers and that do not have // indexed interceptors, we initialize the inlined fast case (if // present) by patching the inlined map check. @@ -1110,7 +1166,16 @@ Object* KeyedStoreIC::Store(State state, bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded(); ASSERT(!(use_ic && object->IsJSGlobalProxy())); - if (use_ic) set_target(generic_stub()); + if (use_ic) { + Code* stub = generic_stub(); + if (object->IsJSObject()) { + Handle receiver = Handle::cast(object); + if (receiver->HasExternalArrayElements()) { + stub = external_array_stub(receiver->GetElementsKind()); + } + } + set_target(stub); + } // Set the property. return Runtime::SetObjectProperty(object, key, value, NONE); diff --git a/src/ic.h b/src/ic.h index fcf1ec0..8709088 100644 --- a/src/ic.h +++ b/src/ic.h @@ -269,6 +269,13 @@ class KeyedLoadIC: public IC { static void GeneratePreMonomorphic(MacroAssembler* masm); static void GenerateGeneric(MacroAssembler* masm); + // Generators for external array types. See objects.h. + // These are similar to the generic IC; they optimize the case of + // operating upon external array types but fall back to the runtime + // for all other types. + static void GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type); + // Clear the use of the inlined version. static void ClearInlinedVersion(Address address); @@ -294,6 +301,7 @@ class KeyedLoadIC: public IC { static Code* pre_monomorphic_stub() { return Builtins::builtin(Builtins::KeyedLoadIC_PreMonomorphic); } + static Code* external_array_stub(JSObject::ElementsKind elements_kind); static void Clear(Address address, Code* target); @@ -358,6 +366,13 @@ class KeyedStoreIC: public IC { static void GenerateGeneric(MacroAssembler* masm); static void GenerateExtendStorage(MacroAssembler* masm); + // Generators for external array types. See objects.h. + // These are similar to the generic IC; they optimize the case of + // operating upon external array types but fall back to the runtime + // for all other types. + static void GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type); + // Clear the inlined version so the IC is always hit. static void ClearInlinedVersion(Address address); @@ -384,6 +399,7 @@ class KeyedStoreIC: public IC { static Code* generic_stub() { return Builtins::builtin(Builtins::KeyedStoreIC_Generic); } + static Code* external_array_stub(JSObject::ElementsKind elements_kind); static void Clear(Address address, Code* target); diff --git a/src/messages.js b/src/messages.js index 2720792..ee19b4e 100644 --- a/src/messages.js +++ b/src/messages.js @@ -159,6 +159,7 @@ function FormatMessage(message) { reduce_no_initial: "Reduce of empty array with no initial value", // RangeError invalid_array_length: "Invalid array length", + invalid_array_index: "Invalid array index %0", // Currently for CanvasArray types only stack_overflow: "Maximum call stack size exceeded", apply_overflow: "Function.prototype.apply cannot support %0 arguments", // SyntaxError diff --git a/src/objects-debug.cc b/src/objects-debug.cc index afa51f6..27c6414 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -274,6 +274,41 @@ void PixelArray::PixelArrayPrint() { } +void ExternalByteArray::ExternalByteArrayPrint() { + PrintF("external byte array"); +} + + +void ExternalUnsignedByteArray::ExternalUnsignedByteArrayPrint() { + PrintF("external unsigned byte array"); +} + + +void ExternalShortArray::ExternalShortArrayPrint() { + PrintF("external short array"); +} + + +void ExternalUnsignedShortArray::ExternalUnsignedShortArrayPrint() { + PrintF("external unsigned short array"); +} + + +void ExternalIntArray::ExternalIntArrayPrint() { + PrintF("external int array"); +} + + +void ExternalUnsignedIntArray::ExternalUnsignedIntArrayPrint() { + PrintF("external unsigned int array"); +} + + +void ExternalFloatArray::ExternalFloatArrayPrint() { + PrintF("external float array"); +} + + void ByteArray::ByteArrayVerify() { ASSERT(IsByteArray()); } @@ -284,6 +319,41 @@ void PixelArray::PixelArrayVerify() { } +void ExternalByteArray::ExternalByteArrayVerify() { + ASSERT(IsExternalByteArray()); +} + + +void ExternalUnsignedByteArray::ExternalUnsignedByteArrayVerify() { + ASSERT(IsExternalUnsignedByteArray()); +} + + +void ExternalShortArray::ExternalShortArrayVerify() { + ASSERT(IsExternalShortArray()); +} + + +void ExternalUnsignedShortArray::ExternalUnsignedShortArrayVerify() { + ASSERT(IsExternalUnsignedShortArray()); +} + + +void ExternalIntArray::ExternalIntArrayVerify() { + ASSERT(IsExternalIntArray()); +} + + +void ExternalUnsignedIntArray::ExternalUnsignedIntArrayVerify() { + ASSERT(IsExternalUnsignedIntArray()); +} + + +void ExternalFloatArray::ExternalFloatArrayVerify() { + ASSERT(IsExternalFloatArray()); +} + + void JSObject::PrintProperties() { if (HasFastProperties()) { DescriptorArray* descs = map()->instance_descriptors(); @@ -345,6 +415,58 @@ void JSObject::PrintElements() { } break; } + case EXTERNAL_BYTE_ELEMENTS: { + ExternalByteArray* p = ExternalByteArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %d\n", i, static_cast(p->get(i))); + } + break; + } + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { + ExternalUnsignedByteArray* p = + ExternalUnsignedByteArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %d\n", i, static_cast(p->get(i))); + } + break; + } + case EXTERNAL_SHORT_ELEMENTS: { + ExternalShortArray* p = ExternalShortArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %d\n", i, static_cast(p->get(i))); + } + break; + } + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { + ExternalUnsignedShortArray* p = + ExternalUnsignedShortArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %d\n", i, static_cast(p->get(i))); + } + break; + } + case EXTERNAL_INT_ELEMENTS: { + ExternalIntArray* p = ExternalIntArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %d\n", i, static_cast(p->get(i))); + } + break; + } + case EXTERNAL_UNSIGNED_INT_ELEMENTS: { + ExternalUnsignedIntArray* p = + ExternalUnsignedIntArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %d\n", i, static_cast(p->get(i))); + } + break; + } + case EXTERNAL_FLOAT_ELEMENTS: { + ExternalFloatArray* p = ExternalFloatArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(" %d: %f\n", i, p->get(i)); + } + break; + } case DICTIONARY_ELEMENTS: elements()->Print(); break; @@ -433,6 +555,16 @@ static const char* TypeToString(InstanceType type) { case FIXED_ARRAY_TYPE: return "FIXED_ARRAY"; case BYTE_ARRAY_TYPE: return "BYTE_ARRAY"; case PIXEL_ARRAY_TYPE: return "PIXEL_ARRAY"; + case EXTERNAL_BYTE_ARRAY_TYPE: return "EXTERNAL_BYTE_ARRAY"; + case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: + return "EXTERNAL_UNSIGNED_BYTE_ARRAY"; + case EXTERNAL_SHORT_ARRAY_TYPE: return "EXTERNAL_SHORT_ARRAY"; + case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: + return "EXTERNAL_UNSIGNED_SHORT_ARRAY"; + case EXTERNAL_INT_ARRAY_TYPE: return "EXTERNAL_INT_ARRAY"; + case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: + return "EXTERNAL_UNSIGNED_INT_ARRAY"; + case EXTERNAL_FLOAT_ARRAY_TYPE: return "EXTERNAL_FLOAT_ARRAY"; case FILLER_TYPE: return "FILLER"; case JS_OBJECT_TYPE: return "JS_OBJECT"; case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT"; diff --git a/src/objects-inl.h b/src/objects-inl.h index cb7b7c8..1ada583 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -360,6 +360,65 @@ bool Object::IsPixelArray() { } +bool Object::IsExternalArray() { + if (!Object::IsHeapObject()) + return false; + InstanceType instance_type = + HeapObject::cast(this)->map()->instance_type(); + return (instance_type >= EXTERNAL_BYTE_ARRAY_TYPE && + instance_type <= EXTERNAL_FLOAT_ARRAY_TYPE); +} + + +bool Object::IsExternalByteArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_BYTE_ARRAY_TYPE; +} + + +bool Object::IsExternalUnsignedByteArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE; +} + + +bool Object::IsExternalShortArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_SHORT_ARRAY_TYPE; +} + + +bool Object::IsExternalUnsignedShortArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE; +} + + +bool Object::IsExternalIntArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_INT_ARRAY_TYPE; +} + + +bool Object::IsExternalUnsignedIntArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_UNSIGNED_INT_ARRAY_TYPE; +} + + +bool Object::IsExternalFloatArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_FLOAT_ARRAY_TYPE; +} + + bool Object::IsFailure() { return HAS_FAILURE_TAG(this); } @@ -1084,14 +1143,16 @@ ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset) Array* JSObject::elements() { Object* array = READ_FIELD(this, kElementsOffset); // In the assert below Dictionary is covered under FixedArray. - ASSERT(array->IsFixedArray() || array->IsPixelArray()); + ASSERT(array->IsFixedArray() || array->IsPixelArray() || + array->IsExternalArray()); return reinterpret_cast(array); } void JSObject::set_elements(Array* value, WriteBarrierMode mode) { // In the assert below Dictionary is covered under FixedArray. - ASSERT(value->IsFixedArray() || value->IsPixelArray()); + ASSERT(value->IsFixedArray() || value->IsPixelArray() || + value->IsExternalArray()); WRITE_FIELD(this, kElementsOffset, value); CONDITIONAL_WRITE_BARRIER(this, kElementsOffset, mode); } @@ -1554,6 +1615,14 @@ CAST_ACCESSOR(JSRegExp) CAST_ACCESSOR(Proxy) CAST_ACCESSOR(ByteArray) CAST_ACCESSOR(PixelArray) +CAST_ACCESSOR(ExternalArray) +CAST_ACCESSOR(ExternalByteArray) +CAST_ACCESSOR(ExternalUnsignedByteArray) +CAST_ACCESSOR(ExternalShortArray) +CAST_ACCESSOR(ExternalUnsignedShortArray) +CAST_ACCESSOR(ExternalIntArray) +CAST_ACCESSOR(ExternalUnsignedIntArray) +CAST_ACCESSOR(ExternalFloatArray) CAST_ACCESSOR(Struct) @@ -1938,6 +2007,116 @@ void PixelArray::set(int index, uint8_t value) { } +void* ExternalArray::external_pointer() { + intptr_t ptr = READ_INTPTR_FIELD(this, kExternalPointerOffset); + return reinterpret_cast(ptr); +} + + +void ExternalArray::set_external_pointer(void* value, WriteBarrierMode mode) { + intptr_t ptr = reinterpret_cast(value); + WRITE_INTPTR_FIELD(this, kExternalPointerOffset, ptr); +} + + +int8_t ExternalByteArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + int8_t* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalByteArray::set(int index, int8_t value) { + ASSERT((index >= 0) && (index < this->length())); + int8_t* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + +uint8_t ExternalUnsignedByteArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + uint8_t* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalUnsignedByteArray::set(int index, uint8_t value) { + ASSERT((index >= 0) && (index < this->length())); + uint8_t* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + +int16_t ExternalShortArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + int16_t* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalShortArray::set(int index, int16_t value) { + ASSERT((index >= 0) && (index < this->length())); + int16_t* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + +uint16_t ExternalUnsignedShortArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + uint16_t* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalUnsignedShortArray::set(int index, uint16_t value) { + ASSERT((index >= 0) && (index < this->length())); + uint16_t* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + +int32_t ExternalIntArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + int32_t* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalIntArray::set(int index, int32_t value) { + ASSERT((index >= 0) && (index < this->length())); + int32_t* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + +uint32_t ExternalUnsignedIntArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + uint32_t* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalUnsignedIntArray::set(int index, uint32_t value) { + ASSERT((index >= 0) && (index < this->length())); + uint32_t* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + +float ExternalFloatArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + float* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalFloatArray::set(int index, float value) { + ASSERT((index >= 0) && (index < this->length())); + float* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + int Map::instance_size() { return READ_BYTE_FIELD(this, kInstanceSizeOffset) << kPointerSizeLog2; } @@ -2646,6 +2825,25 @@ JSObject::ElementsKind JSObject::GetElementsKind() { ASSERT(array->IsDictionary()); return DICTIONARY_ELEMENTS; } + if (array->IsExternalArray()) { + switch (array->map()->instance_type()) { + case EXTERNAL_BYTE_ARRAY_TYPE: + return EXTERNAL_BYTE_ELEMENTS; + case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: + return EXTERNAL_UNSIGNED_BYTE_ELEMENTS; + case EXTERNAL_SHORT_ARRAY_TYPE: + return EXTERNAL_SHORT_ELEMENTS; + case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: + return EXTERNAL_UNSIGNED_SHORT_ELEMENTS; + case EXTERNAL_INT_ARRAY_TYPE: + return EXTERNAL_INT_ELEMENTS; + case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: + return EXTERNAL_UNSIGNED_INT_ELEMENTS; + default: + ASSERT(array->map()->instance_type() == EXTERNAL_FLOAT_ARRAY_TYPE); + return EXTERNAL_FLOAT_ELEMENTS; + } + } ASSERT(array->IsPixelArray()); return PIXEL_ELEMENTS; } @@ -2666,6 +2864,52 @@ bool JSObject::HasPixelElements() { } +bool JSObject::HasExternalArrayElements() { + return (HasExternalByteElements() || + HasExternalUnsignedByteElements() || + HasExternalShortElements() || + HasExternalUnsignedShortElements() || + HasExternalIntElements() || + HasExternalUnsignedIntElements() || + HasExternalFloatElements()); +} + + +bool JSObject::HasExternalByteElements() { + return GetElementsKind() == EXTERNAL_BYTE_ELEMENTS; +} + + +bool JSObject::HasExternalUnsignedByteElements() { + return GetElementsKind() == EXTERNAL_UNSIGNED_BYTE_ELEMENTS; +} + + +bool JSObject::HasExternalShortElements() { + return GetElementsKind() == EXTERNAL_SHORT_ELEMENTS; +} + + +bool JSObject::HasExternalUnsignedShortElements() { + return GetElementsKind() == EXTERNAL_UNSIGNED_SHORT_ELEMENTS; +} + + +bool JSObject::HasExternalIntElements() { + return GetElementsKind() == EXTERNAL_INT_ELEMENTS; +} + + +bool JSObject::HasExternalUnsignedIntElements() { + return GetElementsKind() == EXTERNAL_UNSIGNED_INT_ELEMENTS; +} + + +bool JSObject::HasExternalFloatElements() { + return GetElementsKind() == EXTERNAL_FLOAT_ELEMENTS; +} + + bool JSObject::HasNamedInterceptor() { return map()->has_named_interceptor(); } diff --git a/src/objects.cc b/src/objects.cc index 0c9f965..ca77b7c 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1005,6 +1005,34 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { case PIXEL_ARRAY_TYPE: accumulator->Add("", PixelArray::cast(this)->length()); break; + case EXTERNAL_BYTE_ARRAY_TYPE: + accumulator->Add("", + ExternalByteArray::cast(this)->length()); + break; + case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: + accumulator->Add("", + ExternalUnsignedByteArray::cast(this)->length()); + break; + case EXTERNAL_SHORT_ARRAY_TYPE: + accumulator->Add("", + ExternalShortArray::cast(this)->length()); + break; + case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: + accumulator->Add("", + ExternalUnsignedShortArray::cast(this)->length()); + break; + case EXTERNAL_INT_ARRAY_TYPE: + accumulator->Add("", + ExternalIntArray::cast(this)->length()); + break; + case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: + accumulator->Add("", + ExternalUnsignedIntArray::cast(this)->length()); + break; + case EXTERNAL_FLOAT_ARRAY_TYPE: + accumulator->Add("", + ExternalFloatArray::cast(this)->length()); + break; case SHARED_FUNCTION_INFO_TYPE: accumulator->Add(""); break; @@ -1147,6 +1175,13 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case FILLER_TYPE: case BYTE_ARRAY_TYPE: case PIXEL_ARRAY_TYPE: + case EXTERNAL_BYTE_ARRAY_TYPE: + case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: + case EXTERNAL_SHORT_ARRAY_TYPE: + case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: + case EXTERNAL_INT_ARRAY_TYPE: + case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: + case EXTERNAL_FLOAT_ARRAY_TYPE: break; case SHARED_FUNCTION_INFO_TYPE: { SharedFunctionInfo* shared = reinterpret_cast(this); @@ -2237,7 +2272,7 @@ Object* JSObject::TransformToFastProperties(int unused_property_fields) { Object* JSObject::NormalizeElements() { - ASSERT(!HasPixelElements()); + ASSERT(!HasPixelElements() && !HasExternalArrayElements()); if (HasDictionaryElements()) return this; // Get number of entries. @@ -2322,7 +2357,7 @@ Object* JSObject::DeletePropertyWithInterceptor(String* name) { Object* JSObject::DeleteElementPostInterceptor(uint32_t index, DeleteMode mode) { - ASSERT(!HasPixelElements()); + ASSERT(!HasPixelElements() && !HasExternalArrayElements()); switch (GetElementsKind()) { case FAST_ELEMENTS: { uint32_t length = IsJSArray() ? @@ -2413,10 +2448,17 @@ Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { } break; } - case PIXEL_ELEMENTS: { - // Pixel elements cannot be deleted. Just silently ignore here. + case PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + // Pixel and external array elements cannot be deleted. Just + // silently ignore here. break; - } case DICTIONARY_ELEMENTS: { NumberDictionary* dictionary = element_dictionary(); int entry = dictionary->FindEntry(index); @@ -2507,7 +2549,15 @@ bool JSObject::ReferencesObject(Object* obj) { // Check if the object is among the indexed properties. switch (GetElementsKind()) { case PIXEL_ELEMENTS: - // Raw pixels do not reference other objects. + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + // Raw pixels and external arrays do not reference other + // objects. break; case FAST_ELEMENTS: { int length = IsJSArray() ? @@ -2752,7 +2802,15 @@ Object* JSObject::DefineGetterSetter(String* name, case FAST_ELEMENTS: break; case PIXEL_ELEMENTS: - // Ignore getters and setters on pixel elements. + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + // Ignore getters and setters on pixel and external array + // elements. return Heap::undefined_value(); case DICTIONARY_ELEMENTS: { // Lookup the index. @@ -3087,7 +3145,7 @@ static bool HasKey(FixedArray* array, Object* key) { Object* FixedArray::AddKeysFromJSArray(JSArray* array) { - ASSERT(!array->HasPixelElements()); + ASSERT(!array->HasPixelElements() && !array->HasExternalArrayElements()); switch (array->GetElementsKind()) { case JSObject::FAST_ELEMENTS: return UnionOfKeys(FixedArray::cast(array->elements())); @@ -5201,8 +5259,8 @@ void Code::Disassemble(const char* name) { void JSObject::SetFastElements(FixedArray* elems) { - // We should never end in here with a pixel array. - ASSERT(!HasPixelElements()); + // We should never end in here with a pixel or external array. + ASSERT(!HasPixelElements() && !HasExternalArrayElements()); #ifdef DEBUG // Check the provided array is filled with the_hole. uint32_t len = static_cast(elems->length()); @@ -5239,8 +5297,8 @@ void JSObject::SetFastElements(FixedArray* elems) { Object* JSObject::SetSlowElements(Object* len) { - // We should never end in here with a pixel array. - ASSERT(!HasPixelElements()); + // We should never end in here with a pixel or external array. + ASSERT(!HasPixelElements() && !HasExternalArrayElements()); uint32_t new_length = static_cast(len->Number()); @@ -5318,8 +5376,8 @@ static Object* ArrayLengthRangeError() { Object* JSObject::SetElementsLength(Object* len) { - // We should never end in here with a pixel array. - ASSERT(!HasPixelElements()); + // We should never end in here with a pixel or external array. + ASSERT(!HasPixelElements() && !HasExternalArrayElements()); Object* smi_length = len->ToSmi(); if (smi_length->IsSmi()) { @@ -5420,6 +5478,20 @@ bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) { } break; } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { + // TODO(kbr): Add testcase. + ExternalArray* array = ExternalArray::cast(elements()); + if (index < static_cast(array->length())) { + return true; + } + break; + } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) != NumberDictionary::kNotFound) { @@ -5507,6 +5579,16 @@ bool JSObject::HasLocalElement(uint32_t index) { PixelArray* pixels = PixelArray::cast(elements()); return (index < static_cast(pixels->length())); } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { + ExternalArray* array = ExternalArray::cast(elements()); + return (index < static_cast(array->length())); + } case DICTIONARY_ELEMENTS: { return element_dictionary()->FindEntry(index) != NumberDictionary::kNotFound; @@ -5550,6 +5632,19 @@ bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) { } break; } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { + ExternalArray* array = ExternalArray::cast(elements()); + if (index < static_cast(array->length())) { + return true; + } + break; + } case DICTIONARY_ELEMENTS: { if (element_dictionary()->FindEntry(index) != NumberDictionary::kNotFound) { @@ -5690,6 +5785,37 @@ Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) { PixelArray* pixels = PixelArray::cast(elements()); return pixels->SetValue(index, value); } + case EXTERNAL_BYTE_ELEMENTS: { + ExternalByteArray* array = ExternalByteArray::cast(elements()); + return array->SetValue(index, value); + } + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { + ExternalUnsignedByteArray* array = + ExternalUnsignedByteArray::cast(elements()); + return array->SetValue(index, value); + } + case EXTERNAL_SHORT_ELEMENTS: { + ExternalShortArray* array = ExternalShortArray::cast(elements()); + return array->SetValue(index, value); + } + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { + ExternalUnsignedShortArray* array = + ExternalUnsignedShortArray::cast(elements()); + return array->SetValue(index, value); + } + case EXTERNAL_INT_ELEMENTS: { + ExternalIntArray* array = ExternalIntArray::cast(elements()); + return array->SetValue(index, value); + } + case EXTERNAL_UNSIGNED_INT_ELEMENTS: { + ExternalUnsignedIntArray* array = + ExternalUnsignedIntArray::cast(elements()); + return array->SetValue(index, value); + } + case EXTERNAL_FLOAT_ELEMENTS: { + ExternalFloatArray* array = ExternalFloatArray::cast(elements()); + return array->SetValue(index, value); + } case DICTIONARY_ELEMENTS: { // Insert element in the dictionary. FixedArray* elms = FixedArray::cast(elements()); @@ -5807,6 +5933,17 @@ Object* JSObject::GetElementPostInterceptor(JSObject* receiver, UNIMPLEMENTED(); break; } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { + // TODO(kbr): Add testcase and implement. + UNIMPLEMENTED(); + break; + } case DICTIONARY_ELEMENTS: { NumberDictionary* dictionary = element_dictionary(); int entry = dictionary->FindEntry(index); @@ -5905,6 +6042,65 @@ Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) { } break; } + case EXTERNAL_BYTE_ELEMENTS: { + ExternalByteArray* array = ExternalByteArray::cast(elements()); + if (index < static_cast(array->length())) { + int8_t value = array->get(index); + return Smi::FromInt(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { + ExternalUnsignedByteArray* array = + ExternalUnsignedByteArray::cast(elements()); + if (index < static_cast(array->length())) { + uint8_t value = array->get(index); + return Smi::FromInt(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } + case EXTERNAL_SHORT_ELEMENTS: { + ExternalShortArray* array = ExternalShortArray::cast(elements()); + if (index < static_cast(array->length())) { + int16_t value = array->get(index); + return Smi::FromInt(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { + ExternalUnsignedShortArray* array = + ExternalUnsignedShortArray::cast(elements()); + if (index < static_cast(array->length())) { + uint16_t value = array->get(index); + return Smi::FromInt(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } + case EXTERNAL_INT_ELEMENTS: { + ExternalIntArray* array = ExternalIntArray::cast(elements()); + if (index < static_cast(array->length())) { + int32_t value = array->get(index); + return Heap::NumberFromInt32(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } + case EXTERNAL_UNSIGNED_INT_ELEMENTS: { + ExternalUnsignedIntArray* array = + ExternalUnsignedIntArray::cast(elements()); + if (index < static_cast(array->length())) { + uint32_t value = array->get(index); + return Heap::NumberFromUint32(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } + case EXTERNAL_FLOAT_ELEMENTS: { + ExternalFloatArray* array = ExternalFloatArray::cast(elements()); + if (index < static_cast(array->length())) { + float value = array->get(index); + return Heap::AllocateHeapNumber(value); + } + return Top::Throw(*Factory::NewIndexError(index)); + } case DICTIONARY_ELEMENTS: { NumberDictionary* dictionary = element_dictionary(); int entry = dictionary->FindEntry(index); @@ -5948,7 +6144,14 @@ bool JSObject::HasDenseElements() { } break; } - case PIXEL_ELEMENTS: { + case PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { return true; } case DICTIONARY_ELEMENTS: { @@ -6172,6 +6375,16 @@ bool JSObject::HasRealElementProperty(uint32_t index) { PixelArray* pixels = PixelArray::cast(elements()); return index < static_cast(pixels->length()); } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { + ExternalArray* array = ExternalArray::cast(elements()); + return index < static_cast(array->length()); + } case DICTIONARY_ELEMENTS: { return element_dictionary()->FindEntry(index) != NumberDictionary::kNotFound; @@ -6392,6 +6605,23 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, ASSERT(!storage || storage->length() >= counter); break; } + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: { + int length = ExternalArray::cast(elements())->length(); + while (counter < length) { + if (storage != NULL) { + storage->set(counter, Smi::FromInt(counter), SKIP_WRITE_BARRIER); + } + counter++; + } + ASSERT(!storage || storage->length() >= counter); + break; + } case DICTIONARY_ELEMENTS: { if (storage != NULL) { element_dictionary()->CopyKeysTo(storage, filter); @@ -6938,7 +7168,7 @@ Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { // If the object is in dictionary mode, it is converted to fast elements // mode. Object* JSObject::PrepareElementsForSort(uint32_t limit) { - ASSERT(!HasPixelElements()); + ASSERT(!HasPixelElements() && !HasExternalArrayElements()); if (HasDictionaryElements()) { // Convert to fast elements containing only the existing properties. @@ -7070,6 +7300,105 @@ Object* PixelArray::SetValue(uint32_t index, Object* value) { } +template +static Object* ExternalArrayIntSetter(ExternalArrayClass* receiver, + uint32_t index, + Object* value) { + ValueType cast_value = 0; + if (index < static_cast(receiver->length())) { + if (value->IsSmi()) { + int int_value = Smi::cast(value)->value(); + cast_value = static_cast(int_value); + } else if (value->IsHeapNumber()) { + double double_value = HeapNumber::cast(value)->value(); + cast_value = static_cast(double_value); + } else { + // Clamp undefined to zero (default). All other types have been + // converted to a number type further up in the call chain. + ASSERT(value->IsUndefined()); + } + receiver->set(index, cast_value); + } else { + return Top::Throw(*Factory::NewIndexError(index)); + } + return Heap::NumberFromInt32(cast_value); +} + + +Object* ExternalByteArray::SetValue(uint32_t index, Object* value) { + return ExternalArrayIntSetter + (this, index, value); +} + + +Object* ExternalUnsignedByteArray::SetValue(uint32_t index, Object* value) { + return ExternalArrayIntSetter + (this, index, value); +} + + +Object* ExternalShortArray::SetValue(uint32_t index, Object* value) { + return ExternalArrayIntSetter + (this, index, value); +} + + +Object* ExternalUnsignedShortArray::SetValue(uint32_t index, Object* value) { + return ExternalArrayIntSetter + (this, index, value); +} + + +Object* ExternalIntArray::SetValue(uint32_t index, Object* value) { + return ExternalArrayIntSetter + (this, index, value); +} + + +Object* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) { + uint32_t cast_value = 0; + if (index < static_cast(length())) { + if (value->IsSmi()) { + int int_value = Smi::cast(value)->value(); + cast_value = static_cast(int_value); + } else if (value->IsHeapNumber()) { + double double_value = HeapNumber::cast(value)->value(); + cast_value = static_cast(double_value); + } else { + // Clamp undefined to zero (default). All other types have been + // converted to a number type further up in the call chain. + ASSERT(value->IsUndefined()); + } + set(index, cast_value); + } else { + return Top::Throw(*Factory::NewIndexError(index)); + } + return Heap::NumberFromUint32(cast_value); +} + + +Object* ExternalFloatArray::SetValue(uint32_t index, Object* value) { + float cast_value = 0; + if (index < static_cast(length())) { + if (value->IsSmi()) { + int int_value = Smi::cast(value)->value(); + cast_value = static_cast(int_value); + } else if (value->IsHeapNumber()) { + double double_value = HeapNumber::cast(value)->value(); + cast_value = static_cast(double_value); + } else { + // Clamp undefined to zero (default). All other types have been + // converted to a number type further up in the call chain. + ASSERT(value->IsUndefined()); + } + set(index, cast_value); + } else { + return Top::Throw(*Factory::NewIndexError(index)); + } + return Heap::AllocateHeapNumber(cast_value); +} + + Object* GlobalObject::GetPropertyCell(LookupResult* result) { ASSERT(!HasFastProperties()); Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); diff --git a/src/objects.h b/src/objects.h index deb0971..68bed6c 100644 --- a/src/objects.h +++ b/src/objects.h @@ -56,6 +56,14 @@ // - Array // - ByteArray // - PixelArray +// - ExternalArray +// - ExternalByteArray +// - ExternalUnsignedByteArray +// - ExternalShortArray +// - ExternalUnsignedShortArray +// - ExternalIntArray +// - ExternalUnsignedIntArray +// - ExternalFloatArray // - FixedArray // - DescriptorArray // - HashTable @@ -274,6 +282,16 @@ enum PropertyNormalizationMode { V(PROXY_TYPE) \ V(BYTE_ARRAY_TYPE) \ V(PIXEL_ARRAY_TYPE) \ + /* Note: the order of these external array */ \ + /* types is relied upon in */ \ + /* Object::IsExternalArray(). */ \ + V(EXTERNAL_BYTE_ARRAY_TYPE) \ + V(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) \ + V(EXTERNAL_SHORT_ARRAY_TYPE) \ + V(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) \ + V(EXTERNAL_INT_ARRAY_TYPE) \ + V(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) \ + V(EXTERNAL_FLOAT_ARRAY_TYPE) \ V(FILLER_TYPE) \ \ V(ACCESSOR_INFO_TYPE) \ @@ -673,6 +691,13 @@ enum InstanceType { PROXY_TYPE, BYTE_ARRAY_TYPE, PIXEL_ARRAY_TYPE, + EXTERNAL_BYTE_ARRAY_TYPE, + EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE, + EXTERNAL_SHORT_ARRAY_TYPE, + EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE, + EXTERNAL_INT_ARRAY_TYPE, + EXTERNAL_UNSIGNED_INT_ARRAY_TYPE, + EXTERNAL_FLOAT_ARRAY_TYPE, FILLER_TYPE, SMI_TYPE, @@ -780,6 +805,14 @@ class Object BASE_EMBEDDED { inline bool IsNumber(); inline bool IsByteArray(); inline bool IsPixelArray(); + inline bool IsExternalArray(); + inline bool IsExternalByteArray(); + inline bool IsExternalUnsignedByteArray(); + inline bool IsExternalShortArray(); + inline bool IsExternalUnsignedShortArray(); + inline bool IsExternalIntArray(); + inline bool IsExternalUnsignedIntArray(); + inline bool IsExternalFloatArray(); inline bool IsFailure(); inline bool IsRetryAfterGC(); inline bool IsOutOfMemoryFailure(); @@ -1323,7 +1356,14 @@ class JSObject: public HeapObject { enum ElementsKind { FAST_ELEMENTS, DICTIONARY_ELEMENTS, - PIXEL_ELEMENTS + PIXEL_ELEMENTS, + EXTERNAL_BYTE_ELEMENTS, + EXTERNAL_UNSIGNED_BYTE_ELEMENTS, + EXTERNAL_SHORT_ELEMENTS, + EXTERNAL_UNSIGNED_SHORT_ELEMENTS, + EXTERNAL_INT_ELEMENTS, + EXTERNAL_UNSIGNED_INT_ELEMENTS, + EXTERNAL_FLOAT_ELEMENTS }; // [properties]: Backing storage for properties. @@ -1343,6 +1383,14 @@ class JSObject: public HeapObject { inline bool HasFastElements(); inline bool HasDictionaryElements(); inline bool HasPixelElements(); + inline bool HasExternalArrayElements(); + inline bool HasExternalByteElements(); + inline bool HasExternalUnsignedByteElements(); + inline bool HasExternalShortElements(); + inline bool HasExternalUnsignedShortElements(); + inline bool HasExternalIntElements(); + inline bool HasExternalUnsignedIntElements(); + inline bool HasExternalFloatElements(); inline NumberDictionary* element_dictionary(); // Gets slow elements. // Collects elements starting at index 0. @@ -2507,6 +2555,200 @@ class PixelArray: public Array { }; +// An ExternalArray represents a fixed-size array of primitive values +// which live outside the JavaScript heap. Its subclasses are used to +// implement the CanvasArray types being defined in the WebGL +// specification. As of this writing the first public draft is not yet +// available, but Khronos members can access the draft at: +// https://cvs.khronos.org/svn/repos/3dweb/trunk/doc/spec/WebGL-spec.html +// +// The semantics of these arrays differ from CanvasPixelArray. +// Out-of-range values passed to the setter are converted via a C +// cast, not clamping. Out-of-range indices cause exceptions to be +// raised rather than being silently ignored. +class ExternalArray: public Array { + public: + // [external_pointer]: The pointer to the external memory area backing this + // external array. + DECL_ACCESSORS(external_pointer, void) // Pointer to the data store. + + // Casting. + static inline ExternalArray* cast(Object* obj); + + // Maximal acceptable length for an external array. + static const int kMaxLength = 0x3fffffff; + + // ExternalArray headers are not quadword aligned. + static const int kExternalPointerOffset = Array::kAlignedSize; + static const int kHeaderSize = kExternalPointerOffset + kPointerSize; + static const int kAlignedSize = OBJECT_SIZE_ALIGN(kHeaderSize); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalArray); +}; + + +class ExternalByteArray: public ExternalArray { + public: + // Setter and getter. + inline int8_t get(int index); + inline void set(int index, int8_t value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalByteArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalByteArrayPrint(); + void ExternalByteArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalByteArray); +}; + + +class ExternalUnsignedByteArray: public ExternalArray { + public: + // Setter and getter. + inline uint8_t get(int index); + inline void set(int index, uint8_t value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalUnsignedByteArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalUnsignedByteArrayPrint(); + void ExternalUnsignedByteArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalUnsignedByteArray); +}; + + +class ExternalShortArray: public ExternalArray { + public: + // Setter and getter. + inline int16_t get(int index); + inline void set(int index, int16_t value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalShortArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalShortArrayPrint(); + void ExternalShortArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalShortArray); +}; + + +class ExternalUnsignedShortArray: public ExternalArray { + public: + // Setter and getter. + inline uint16_t get(int index); + inline void set(int index, uint16_t value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalUnsignedShortArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalUnsignedShortArrayPrint(); + void ExternalUnsignedShortArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalUnsignedShortArray); +}; + + +class ExternalIntArray: public ExternalArray { + public: + // Setter and getter. + inline int32_t get(int index); + inline void set(int index, int32_t value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalIntArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalIntArrayPrint(); + void ExternalIntArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalIntArray); +}; + + +class ExternalUnsignedIntArray: public ExternalArray { + public: + // Setter and getter. + inline uint32_t get(int index); + inline void set(int index, uint32_t value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalUnsignedIntArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalUnsignedIntArrayPrint(); + void ExternalUnsignedIntArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalUnsignedIntArray); +}; + + +class ExternalFloatArray: public ExternalArray { + public: + // Setter and getter. + inline float get(int index); + inline void set(int index, float value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + Object* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalFloatArray* cast(Object* obj); + +#ifdef DEBUG + void ExternalFloatArrayPrint(); + void ExternalFloatArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalFloatArray); +}; + + // Code describes objects with on-the-fly generated machine code. class Code: public HeapObject { public: diff --git a/src/runtime.cc b/src/runtime.cc index 9eeffd1..93d6912 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -156,7 +156,7 @@ static Object* DeepCopyBoilerplate(JSObject* boilerplate) { // Deep copy local elements. // Pixel elements cannot be created using an object literal. - ASSERT(!copy->HasPixelElements()); + ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements()); switch (copy->GetElementsKind()) { case JSObject::FAST_ELEMENTS: { FixedArray* elements = FixedArray::cast(copy->elements()); @@ -2580,6 +2580,18 @@ Object* Runtime::GetObjectProperty(Handle object, Handle key) { return GetElementOrCharAt(object, index); } + // If the target object is a JSObject and has an ExternalArray as + // its elements, we need to check for negative indices and report + // exceptions. Indices larger than the array's length will be caught + // elsewhere. + if (key->IsSmi() && Smi::cast(*key)->value() < 0) { + if (object->IsJSObject() && + JSObject::cast(*object)->HasExternalArrayElements()) { + uint32_t index = static_cast(Smi::cast(*key)->value()); + return Top::Throw(*Factory::NewIndexError(index)); + } + } + // Convert the key to a string - possibly by calling back into JavaScript. Handle name; if (key->IsString()) { @@ -2714,6 +2726,18 @@ Object* Runtime::SetObjectProperty(Handle object, return *value; } + // If the target object is a JSObject and has an ExternalArray as + // its elements, we need to check for negative indices and report + // exceptions. Indices larger than the array's length will be caught + // elsewhere. + if (key->IsSmi() && Smi::cast(*key)->value() < 0) { + if (object->IsJSObject() && + JSObject::cast(*object)->HasExternalArrayElements()) { + uint32_t index = static_cast(Smi::cast(*key)->value()); + return Top::Throw(*Factory::NewIndexError(index)); + } + } + if (key->IsString()) { Handle result; if (Handle::cast(key)->AsArrayIndex(&index)) { @@ -5273,6 +5297,47 @@ class ArrayConcatVisitor { }; +template +static uint32_t IterateExternalArrayElements(Handle receiver, + bool elements_are_ints, + bool elements_are_guaranteed_smis, + uint32_t range, + ArrayConcatVisitor* visitor) { + Handle array( + ExternalArrayClass::cast(receiver->elements())); + uint32_t len = Min(static_cast(array->length()), range); + + if (visitor != NULL) { + if (elements_are_ints) { + if (elements_are_guaranteed_smis) { + for (uint32_t j = 0; j < len; j++) { + Handle e(Smi::FromInt(static_cast(array->get(j)))); + visitor->visit(j, e); + } + } else { + for (uint32_t j = 0; j < len; j++) { + int64_t val = static_cast(array->get(j)); + if (Smi::IsValid(static_cast(val))) { + Handle e(Smi::FromInt(static_cast(val))); + visitor->visit(j, e); + } else { + Handle e( + Heap::AllocateHeapNumber(static_cast(val))); + visitor->visit(j, e); + } + } + } + } else { + for (uint32_t j = 0; j < len; j++) { + Handle e(Heap::AllocateHeapNumber(array->get(j))); + visitor->visit(j, e); + } + } + } + + return len; +} + /** * A helper function that visits elements of a JSObject. Only elements * whose index between 0 and range (exclusive) are visited. @@ -5322,6 +5387,48 @@ static uint32_t IterateElements(Handle receiver, } break; } + case JSObject::EXTERNAL_BYTE_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, true, true, range, visitor); + break; + } + case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, true, true, range, visitor); + break; + } + case JSObject::EXTERNAL_SHORT_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, true, true, range, visitor); + break; + } + case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, true, true, range, visitor); + break; + } + case JSObject::EXTERNAL_INT_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, true, false, range, visitor); + break; + } + case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, true, false, range, visitor); + break; + } + case JSObject::EXTERNAL_FLOAT_ELEMENTS: { + num_of_elements = + IterateExternalArrayElements( + receiver, false, false, range, visitor); + break; + } case JSObject::DICTIONARY_ELEMENTS: { Handle dict(receiver->element_dictionary()); uint32_t capacity = dict->Capacity(); diff --git a/src/v8-counters.h b/src/v8-counters.h index 84f9ee4..b3f29f5 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -118,6 +118,7 @@ namespace internal { SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \ SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \ SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \ + SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \ /* Count how much the monomorphic keyed-load stubs are hit. */ \ SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \ SC(keyed_load_string_length, V8.KeyedLoadStringLength) \ diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 3f3d34e..eaa3004 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -1255,6 +1255,15 @@ void Assembler::movb(const Operand& dst, Register src) { emit_operand(src, dst); } +void Assembler::movw(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x66); + emit_optional_rex_32(src, dst); + emit(0x89); + emit_operand(src, dst); +} + void Assembler::movl(Register dst, const Operand& src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -1439,6 +1448,26 @@ void Assembler::movq(Register dst, Handle value, RelocInfo::Mode mode) { } +void Assembler::movsxbq(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_32(dst, src); + emit(0x0F); + emit(0xBE); + emit_operand(dst, src); +} + + +void Assembler::movsxwq(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x0F); + emit(0xBF); + emit_operand(dst, src); +} + + void Assembler::movsxlq(Register dst, Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -1477,6 +1506,16 @@ void Assembler::movzxbl(Register dst, const Operand& src) { } +void Assembler::movzxwq(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x0F); + emit(0xB7); + emit_operand(dst, src); +} + + void Assembler::movzxwl(Register dst, const Operand& src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -2021,7 +2060,7 @@ void Assembler::fistp_d(const Operand& adr) { last_pc_ = pc_; emit_optional_rex_32(adr); emit(0xDF); - emit_operand(8, adr); + emit_operand(7, adr); } diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 7e09a41..a24f267 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -513,6 +513,10 @@ class Assembler : public Malloced { void movb(Register dst, Immediate imm); void movb(const Operand& dst, Register src); + // Move the low 16 bits of a 64-bit register value to a 16-bit + // memory location. + void movw(const Operand& dst, Register src); + void movl(Register dst, Register src); void movl(Register dst, const Operand& src); void movl(const Operand& dst, Register src); @@ -542,10 +546,13 @@ class Assembler : public Malloced { void movq(Register dst, ExternalReference ext); void movq(Register dst, Handle handle, RelocInfo::Mode rmode); + void movsxbq(Register dst, const Operand& src); + void movsxwq(Register dst, const Operand& src); void movsxlq(Register dst, Register src); void movsxlq(Register dst, const Operand& src); void movzxbq(Register dst, const Operand& src); void movzxbl(Register dst, const Operand& src); + void movzxwq(Register dst, const Operand& src); void movzxwl(Register dst, const Operand& src); // New x64 instruction to load from an immediate 64-bit pointer into RAX. diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index d72257e..527e9c0 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -240,13 +240,6 @@ class FloatingPointHelper : public AllStatic { // operands, jumps to the non_float label otherwise. static void CheckNumberOperands(MacroAssembler* masm, Label* non_float); - - // Allocate a heap number in new space with undefined value. - // Returns tagged pointer in result, or jumps to need_gc if new space is full. - static void AllocateHeapNumber(MacroAssembler* masm, - Label* need_gc, - Register scratch, - Register result); }; @@ -3975,10 +3968,9 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* args) { // Allocate heap number for result if possible. Result scratch = allocator()->Allocate(); Result heap_number = allocator()->Allocate(); - FloatingPointHelper::AllocateHeapNumber(masm_, - call_runtime.entry_label(), - scratch.reg(), - heap_number.reg()); + __ AllocateHeapNumber(heap_number.reg(), + scratch.reg(), + call_runtime.entry_label()); scratch.Unuse(); // Store the result in the allocated heap number. @@ -6363,7 +6355,7 @@ void UnarySubStub::Generate(MacroAssembler* masm) { if (overwrite_) { __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rdx); } else { - FloatingPointHelper::AllocateHeapNumber(masm, &slow, rbx, rcx); + __ AllocateHeapNumber(rcx, rbx, &slow); // rcx: allocated 'empty' number __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx); __ movq(rax, rcx); @@ -7210,24 +7202,6 @@ void StackCheckStub::Generate(MacroAssembler* masm) { } -void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm, - Label* need_gc, - Register scratch, - Register result) { - // Allocate heap number in new space. - __ AllocateInNewSpace(HeapNumber::kSize, - result, - scratch, - no_reg, - need_gc, - TAG_OBJECT); - - // Set the map and tag the result. - __ LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex); - __ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); -} - - void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, Register number) { Label load_smi, done; @@ -7487,10 +7461,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ JumpIfNotSmi(rax, &skip_allocation); // Fall through! case NO_OVERWRITE: - FloatingPointHelper::AllocateHeapNumber(masm, - &call_runtime, - rcx, - rax); + __ AllocateHeapNumber(rax, rcx, &call_runtime); __ bind(&skip_allocation); break; default: UNREACHABLE(); @@ -7606,8 +7577,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ JumpIfNotSmi(rax, &skip_allocation); // Fall through! case NO_OVERWRITE: - FloatingPointHelper::AllocateHeapNumber(masm, &call_runtime, - rcx, rax); + __ AllocateHeapNumber(rax, rcx, &call_runtime); __ bind(&skip_allocation); break; default: UNREACHABLE(); diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 7108025..4995572 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -320,7 +320,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: Load name and receiver from stack and jump to runtime. __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_generic_slow, 1); - KeyedLoadIC::Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); + Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); __ bind(&check_string); // The key is not a smi. // Is it a string? @@ -360,6 +360,12 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { } +void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type) { + GenerateGeneric(masm); +} + + void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rsp[0] : return address @@ -458,15 +464,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // rbx: index (as a smi) __ j(below, &fast); - // Slow case: Push extra copies of the arguments (3). + // Slow case: call runtime. __ bind(&slow); - __ pop(rcx); - __ push(Operand(rsp, 1 * kPointerSize)); - __ push(Operand(rsp, 1 * kPointerSize)); - __ push(rax); - __ push(rcx); - // Do tail-call to runtime routine. - __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); + Generate(masm, ExternalReference(Runtime::kSetProperty)); // Check whether the elements is a pixel array. // rax: value @@ -558,6 +558,12 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { } +void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, + ExternalArrayType array_type) { + GenerateGeneric(masm); +} + + void CallIC::Generate(MacroAssembler* masm, int argc, ExternalReference const& f) { diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index a903f7a..25c9dcf 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -580,6 +580,14 @@ Condition MacroAssembler::CheckInteger32ValidSmiValue(Register src) { } +Condition MacroAssembler::CheckUInteger32ValidSmiValue(Register src) { + // An unsigned 32-bit integer value is valid as long as the high bit + // is not set. + testq(src, Immediate(0x80000000)); + return zero; +} + + void MacroAssembler::SmiNeg(Register dst, Register src, Label* on_smi_result) { if (dst.is(src)) { ASSERT(!dst.is(kScratchRegister)); @@ -1243,6 +1251,13 @@ void MacroAssembler::JumpIfNotValidSmiValue(Register src, Label* on_invalid) { } +void MacroAssembler::JumpIfUIntNotValidSmiValue(Register src, + Label* on_invalid) { + Condition is_valid = CheckUInteger32ValidSmiValue(src); + j(NegateCondition(is_valid), on_invalid); +} + + void MacroAssembler::JumpIfNotBothSmi(Register src1, Register src2, Label* on_not_both_smi) { Condition both_smi = CheckBothSmi(src1, src2); @@ -2213,6 +2228,23 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) { } +void MacroAssembler::AllocateHeapNumber(Register result, + Register scratch, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(HeapNumber::kSize, + result, + scratch, + no_reg, + gc_required, + TAG_OBJECT); + + // Set the map. + LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex); + movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); +} + + CodePatcher::CodePatcher(byte* address, int size) : address_(address), size_(size), masm_(address, size + Assembler::kGap) { // Create a new macro assembler pointing to the address of the code to patch. diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 2e65f7a..4c2f35b 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -207,12 +207,19 @@ class MacroAssembler: public Assembler { // to a smi. Condition CheckInteger32ValidSmiValue(Register src); + // Checks whether an 32-bit unsigned integer value is a valid for + // conversion to a smi. + Condition CheckUInteger32ValidSmiValue(Register src); + // Test-and-jump functions. Typically combines a check function // above with a conditional jump. // Jump if the value cannot be represented by a smi. void JumpIfNotValidSmiValue(Register src, Label* on_invalid); + // Jump if the unsigned integer value cannot be represented by a smi. + void JumpIfUIntNotValidSmiValue(Register src, Label* on_invalid); + // Jump to label if the value is a tagged smi. void JumpIfSmi(Register src, Label* on_smi); @@ -498,6 +505,13 @@ class MacroAssembler: public Assembler { // un-done. void UndoAllocationInNewSpace(Register object); + // Allocate a heap number in new space with undefined value. Returns + // tagged pointer in result register, or jumps to gc_required if new + // space is full. + void AllocateHeapNumber(Register result, + Register scratch, + Label* gc_required); + // --------------------------------------------------------------------------- // Support functions. diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 945d00a..41bd1bb 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -25,6 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include + #include "v8.h" #include "api.h" @@ -33,6 +35,7 @@ #include "snapshot.h" #include "platform.h" #include "top.h" +#include "utils.h" #include "cctest.h" static bool IsNaN(double x) { @@ -8039,6 +8042,324 @@ THREADED_TEST(PixelArray) { } +template +static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, + int64_t low, + int64_t high) { + v8::HandleScope scope; + LocalContext context; + const int kElementCount = 40; + int element_size = 0; + switch (array_type) { + case v8::kExternalByteArray: + case v8::kExternalUnsignedByteArray: + element_size = 1; + break; + case v8::kExternalShortArray: + case v8::kExternalUnsignedShortArray: + element_size = 2; + break; + case v8::kExternalIntArray: + case v8::kExternalUnsignedIntArray: + case v8::kExternalFloatArray: + element_size = 4; + break; + default: + UNREACHABLE(); + break; + } + ElementType* array_data = + static_cast(malloc(kElementCount * element_size)); + i::Handle array = + i::Handle::cast( + i::Factory::NewExternalArray(kElementCount, array_type, array_data)); + i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + for (int i = 0; i < kElementCount; i++) { + array->set(i, static_cast(i)); + } + i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + for (int i = 0; i < kElementCount; i++) { + CHECK_EQ(static_cast(i), static_cast(array->get(i))); + CHECK_EQ(static_cast(i), static_cast(array_data[i])); + } + + v8::Handle obj = v8::Object::New(); + i::Handle jsobj = v8::Utils::OpenHandle(*obj); + // Set the elements to be the external array. + obj->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + CHECK_EQ(1, static_cast(jsobj->GetElement(1)->Number())); + obj->Set(v8_str("field"), v8::Int32::New(1503)); + context->Global()->Set(v8_str("ext_array"), obj); + v8::Handle result = CompileRun("ext_array.field"); + CHECK_EQ(1503, result->Int32Value()); + result = CompileRun("ext_array[1]"); + CHECK_EQ(1, result->Int32Value()); + + // Check pass through of assigned smis + result = CompileRun("var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " sum += ext_array[i] = ext_array[i] = -i;" + "}" + "sum;"); + CHECK_EQ(-28, result->Int32Value()); + + // Check assigned smis + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[i] = i;" + "}" + "var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + // Check assigned smis in reverse order + result = CompileRun("for (var i = 8; --i >= 0; ) {" + " ext_array[i] = i;" + "}" + "var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + // Check pass through of assigned HeapNumbers + result = CompileRun("var sum = 0;" + "for (var i = 0; i < 16; i+=2) {" + " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" + "}" + "sum;"); + CHECK_EQ(-28, result->Int32Value()); + + // Check assigned HeapNumbers + result = CompileRun("for (var i = 0; i < 16; i+=2) {" + " ext_array[i] = (i * 0.5);" + "}" + "var sum = 0;" + "for (var i = 0; i < 16; i+=2) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + // Check assigned HeapNumbers in reverse order + result = CompileRun("for (var i = 14; i >= 0; i-=2) {" + " ext_array[i] = (i * 0.5);" + "}" + "var sum = 0;" + "for (var i = 0; i < 16; i+=2) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + i::ScopedVector test_buf(1024); + + // Check legal boundary conditions. + // The repeated loads and stores ensure the ICs are exercised. + const char* boundary_program = + "var res = 0;" + "for (var i = 0; i < 16; i++) {" + " ext_array[i] = %lld;" + " if (i > 8) {" + " res = ext_array[i];" + " }" + "}" + "res;"; + i::OS::SNPrintF(test_buf, + boundary_program, + low); + result = CompileRun(test_buf.start()); + CHECK_EQ(low, result->IntegerValue()); + + i::OS::SNPrintF(test_buf, + boundary_program, + high); + result = CompileRun(test_buf.start()); + CHECK_EQ(high, result->IntegerValue()); + + // Check misprediction of type in IC. + result = CompileRun("var tmp_array = ext_array;" + "var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " tmp_array[i] = i;" + " sum += tmp_array[i];" + " if (i == 4) {" + " tmp_array = {};" + " }" + "}" + "sum;"); + i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + CHECK_EQ(28, result->Int32Value()); + + // Check out-of-range loads. + result = CompileRun("var caught_exception = false;" + "try {" + " ext_array[-1];" + "} catch (e) {" + " caught_exception = true;" + "}" + "caught_exception;"); + CHECK_EQ(true, result->BooleanValue()); + + i::OS::SNPrintF(test_buf, + "var caught_exception = false;" + "try {" + " ext_array[%d];" + "} catch (e) {" + " caught_exception = true;" + "}" + "caught_exception;", + kElementCount); + result = CompileRun(test_buf.start()); + CHECK_EQ(true, result->BooleanValue()); + + // Check out-of-range stores. + result = CompileRun("var caught_exception = false;" + "try {" + " ext_array[-1] = 1;" + "} catch (e) {" + " caught_exception = true;" + "}" + "caught_exception;"); + CHECK_EQ(true, result->BooleanValue()); + + i::OS::SNPrintF(test_buf, + "var caught_exception = false;" + "try {" + " ext_array[%d] = 1;" + "} catch (e) {" + " caught_exception = true;" + "}" + "caught_exception;", + kElementCount); + result = CompileRun(test_buf.start()); + CHECK_EQ(true, result->BooleanValue()); + + // TODO(kbr): check what happens during IC misses on the type of the object. + // Re-assign array object halfway through a loop. + + // Check other boundary conditions, values and operations. + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[7] = undefined;" + "}" + "ext_array[7];"); + CHECK_EQ(0, result->Int32Value()); + CHECK_EQ(0, static_cast(jsobj->GetElement(7)->Number())); + + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[6] = '2.3';" + "}" + "ext_array[6];"); + CHECK_EQ(2, result->Int32Value()); + CHECK_EQ(2, static_cast(jsobj->GetElement(6)->Number())); + + // Result for storing NaNs and +/-Infinity isn't defined for these + // types; they do not have the clamping behavior CanvasPixelArray + // specifies. + + result = CompileRun("ext_array[3] = 33;" + "delete ext_array[3];" + "ext_array[3];"); + CHECK_EQ(33, result->Int32Value()); + + result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" + "ext_array[2] = 12; ext_array[3] = 13;" + "ext_array.__defineGetter__('2'," + "function() { return 120; });" + "ext_array[2];"); + CHECK_EQ(12, result->Int32Value()); + + result = CompileRun("var js_array = new Array(40);" + "js_array[0] = 77;" + "js_array;"); + CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); + + result = CompileRun("ext_array[1] = 23;" + "ext_array.__proto__ = [];" + "js_array.__proto__ = ext_array;" + "js_array.concat(ext_array);"); + CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); + CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); + + result = CompileRun("ext_array[1] = 23;"); + CHECK_EQ(23, result->Int32Value()); + + free(array_data); +} + + +THREADED_TEST(ExternalByteArray) { + ExternalArrayTestHelper( + v8::kExternalByteArray, + -128, + 127); +} + + +THREADED_TEST(ExternalUnsignedByteArray) { + ExternalArrayTestHelper( + v8::kExternalUnsignedByteArray, + 0, + 255); +} + + +THREADED_TEST(ExternalShortArray) { + ExternalArrayTestHelper( + v8::kExternalShortArray, + -32768, + 32767); +} + + +THREADED_TEST(ExternalUnsignedShortArray) { + ExternalArrayTestHelper( + v8::kExternalUnsignedShortArray, + 0, + 65535); +} + + +THREADED_TEST(ExternalIntArray) { + ExternalArrayTestHelper( + v8::kExternalIntArray, + INT_MIN, // -2147483648 + INT_MAX); // 2147483647 +} + + +THREADED_TEST(ExternalUnsignedIntArray) { + ExternalArrayTestHelper( + v8::kExternalUnsignedIntArray, + 0, + UINT_MAX); // 4294967295 +} + + +THREADED_TEST(ExternalFloatArray) { + ExternalArrayTestHelper( + v8::kExternalFloatArray, + -500, + 500); +} + + +THREADED_TEST(ExternalArrays) { + TestExternalByteArray(); + TestExternalUnsignedByteArray(); + TestExternalShortArray(); + TestExternalUnsignedShortArray(); + TestExternalIntArray(); + TestExternalUnsignedIntArray(); + TestExternalFloatArray(); +} + + THREADED_TEST(ScriptContextDependence) { v8::HandleScope scope; LocalContext c1; -- 2.7.4