From: jkummerow@chromium.org Date: Thu, 21 Apr 2011 07:15:43 +0000 (+0000) Subject: Support Float64Arrays X-Git-Tag: upstream/4.7.83~19580 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bc8f6943bbca8a03c82c0aac1084b95a80a1f395;p=platform%2Fupstream%2Fv8.git Support Float64Arrays BUG=None TEST=mjsunit/external-arrays.js; updated cctest; existing unit tests Review URL: http://codereview.chromium.org/6879009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7675 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index d15d024dc..71c61f921 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1445,6 +1445,7 @@ enum ExternalArrayType { kExternalIntArray, kExternalUnsignedIntArray, kExternalFloatArray, + kExternalDoubleArray, kExternalPixelArray }; @@ -3584,7 +3585,7 @@ class Internals { static const int kFullStringRepresentationMask = 0x07; static const int kExternalTwoByteRepresentationTag = 0x02; - static const int kJSObjectType = 0xa0; + static const int kJSObjectType = 0xa1; static const int kFirstNonstringType = 0x80; static const int kProxyType = 0x85; diff --git a/samples/shell.cc b/samples/shell.cc index 222eeda11..51053aa0e 100644 --- a/samples/shell.cc +++ b/samples/shell.cc @@ -72,6 +72,7 @@ v8::Handle Uint16Array(const v8::Arguments& args); v8::Handle Int32Array(const v8::Arguments& args); v8::Handle Uint32Array(const v8::Arguments& args); v8::Handle Float32Array(const v8::Arguments& args); +v8::Handle Float64Array(const v8::Arguments& args); v8::Handle PixelArray(const v8::Arguments& args); v8::Handle ReadFile(const char* name); void ReportException(v8::TryCatch* handler); @@ -359,6 +360,8 @@ v8::Persistent CreateShellContext() { v8::FunctionTemplate::New(Uint32Array)); global->Set(v8::String::New("Float32Array"), v8::FunctionTemplate::New(Float32Array)); + global->Set(v8::String::New("Float64Array"), + v8::FunctionTemplate::New(Float64Array)); global->Set(v8::String::New("PixelArray"), v8::FunctionTemplate::New(PixelArray)); @@ -512,6 +515,12 @@ v8::Handle Float32Array(const v8::Arguments& args) { } +v8::Handle Float64Array(const v8::Arguments& args) { + return CreateExternalArray(args, v8::kExternalDoubleArray, + sizeof(double)); // NOLINT +} + + v8::Handle PixelArray(const v8::Arguments& args) { return CreateExternalArray(args, v8::kExternalPixelArray, sizeof(uint8_t)); } diff --git a/src/api.cc b/src/api.cc index 1a5217482..aa36641f7 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3161,6 +3161,8 @@ ExternalArrayType v8::Object::GetIndexedPropertiesExternalArrayDataType() { return kExternalUnsignedIntArray; case i::EXTERNAL_FLOAT_ARRAY_TYPE: return kExternalFloatArray; + case i::EXTERNAL_DOUBLE_ARRAY_TYPE: + return kExternalDoubleArray; case i::EXTERNAL_PIXEL_ARRAY_TYPE: return kExternalPixelArray; default: diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h index 9050c2c5d..27350fe78 100644 --- a/src/arm/assembler-arm.h +++ b/src/arm/assembler-arm.h @@ -72,6 +72,7 @@ namespace internal { struct Register { static const int kNumRegisters = 16; static const int kNumAllocatableRegisters = 8; + static const int kSizeInBytes = 4; static int ToAllocationIndex(Register reg) { ASSERT(reg.code() < kNumAllocatableRegisters); diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index d66daea0a..2f72d51bf 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -364,136 +364,6 @@ void ConvertToDoubleStub::Generate(MacroAssembler* masm) { } -class FloatingPointHelper : public AllStatic { - public: - - enum Destination { - kVFPRegisters, - kCoreRegisters - }; - - - // Loads smis from r0 and r1 (right and left in binary operations) into - // floating point registers. Depending on the destination the values ends up - // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is - // floating point registers VFP3 must be supported. If core registers are - // requested when VFP3 is supported d6 and d7 will be scratched. - static void LoadSmis(MacroAssembler* masm, - Destination destination, - Register scratch1, - Register scratch2); - - // Loads objects from r0 and r1 (right and left in binary operations) into - // floating point registers. Depending on the destination the values ends up - // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is - // floating point registers VFP3 must be supported. If core registers are - // requested when VFP3 is supported d6 and d7 will still be scratched. If - // either r0 or r1 is not a number (not smi and not heap number object) the - // not_number label is jumped to with r0 and r1 intact. - static void LoadOperands(MacroAssembler* masm, - FloatingPointHelper::Destination destination, - Register heap_number_map, - Register scratch1, - Register scratch2, - Label* not_number); - - // Convert the smi or heap number in object to an int32 using the rules - // for ToInt32 as described in ECMAScript 9.5.: the value is truncated - // and brought into the range -2^31 .. +2^31 - 1. - static void ConvertNumberToInt32(MacroAssembler* masm, - Register object, - Register dst, - Register heap_number_map, - Register scratch1, - Register scratch2, - Register scratch3, - DwVfpRegister double_scratch, - Label* not_int32); - - // Load the number from object into double_dst in the double format. - // Control will jump to not_int32 if the value cannot be exactly represented - // by a 32-bit integer. - // Floating point value in the 32-bit integer range that are not exact integer - // won't be loaded. - static void LoadNumberAsInt32Double(MacroAssembler* masm, - Register object, - Destination destination, - DwVfpRegister double_dst, - Register dst1, - Register dst2, - Register heap_number_map, - Register scratch1, - Register scratch2, - SwVfpRegister single_scratch, - Label* not_int32); - - // Loads the number from object into dst as a 32-bit integer. - // Control will jump to not_int32 if the object cannot be exactly represented - // by a 32-bit integer. - // Floating point value in the 32-bit integer range that are not exact integer - // won't be converted. - // scratch3 is not used when VFP3 is supported. - static void LoadNumberAsInt32(MacroAssembler* masm, - Register object, - Register dst, - Register heap_number_map, - Register scratch1, - Register scratch2, - Register scratch3, - DwVfpRegister double_scratch, - Label* not_int32); - - // Generate non VFP3 code to check if a double can be exactly represented by a - // 32-bit integer. This does not check for 0 or -0, which need - // to be checked for separately. - // Control jumps to not_int32 if the value is not a 32-bit integer, and falls - // through otherwise. - // src1 and src2 will be cloberred. - // - // Expected input: - // - src1: higher (exponent) part of the double value. - // - src2: lower (mantissa) part of the double value. - // Output status: - // - dst: 32 higher bits of the mantissa. (mantissa[51:20]) - // - src2: contains 1. - // - other registers are clobbered. - static void DoubleIs32BitInteger(MacroAssembler* masm, - Register src1, - Register src2, - Register dst, - Register scratch, - Label* not_int32); - - // Generates code to call a C function to do a double operation using core - // registers. (Used when VFP3 is not supported.) - // This code never falls through, but returns with a heap number containing - // the result in r0. - // Register heapnumber_result must be a heap number in which the - // result of the operation will be stored. - // Requires the following layout on entry: - // r0: Left value (least significant part of mantissa). - // r1: Left value (sign, exponent, top of mantissa). - // r2: Right value (least significant part of mantissa). - // r3: Right value (sign, exponent, top of mantissa). - static void CallCCodeForDoubleOperation(MacroAssembler* masm, - Token::Value op, - Register heap_number_result, - Register scratch); - - private: - static void LoadNumber(MacroAssembler* masm, - FloatingPointHelper::Destination destination, - Register object, - DwVfpRegister dst, - Register dst1, - Register dst2, - Register heap_number_map, - Register scratch1, - Register scratch2, - Label* not_number); -}; - - void FloatingPointHelper::LoadSmis(MacroAssembler* masm, FloatingPointHelper::Destination destination, Register scratch1, @@ -651,30 +521,21 @@ void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm, } -void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, - Register object, - Destination destination, - DwVfpRegister double_dst, - Register dst1, - Register dst2, - Register heap_number_map, - Register scratch1, - Register scratch2, - SwVfpRegister single_scratch, - Label* not_int32) { - ASSERT(!scratch1.is(object) && !scratch2.is(object)); - ASSERT(!scratch1.is(scratch2)); - ASSERT(!heap_number_map.is(object) && - !heap_number_map.is(scratch1) && - !heap_number_map.is(scratch2)); +void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm, + Register int_scratch, + Destination destination, + DwVfpRegister double_dst, + Register dst1, + Register dst2, + Register scratch2, + SwVfpRegister single_scratch) { + ASSERT(!smi_scratch.is(scratch2)); - Label done, obj_is_not_smi; + Label done; - __ JumpIfNotSmi(object, &obj_is_not_smi); - __ SmiUntag(scratch1, object); if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); - __ vmov(single_scratch, scratch1); + __ vmov(single_scratch, int_scratch); __ vcvt_f64_s32(double_dst, single_scratch); if (destination == kCoreRegisters) { __ vmov(dst1, dst2, double_dst); @@ -686,20 +547,20 @@ void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, // | s | exp | mantissa | // Check for zero. - __ cmp(scratch1, Operand(0)); - __ mov(dst2, scratch1); - __ mov(dst1, scratch1); + __ cmp(int_scratch, Operand(0)); + __ mov(dst2, int_scratch); + __ mov(dst1, int_scratch); __ b(eq, &done); // Preload the sign of the value. - __ and_(dst2, scratch1, Operand(HeapNumber::kSignMask), SetCC); + __ and_(dst2, int_scratch, Operand(HeapNumber::kSignMask), SetCC); // Get the absolute value of the object (as an unsigned integer). - __ rsb(scratch1, scratch1, Operand(0), SetCC, mi); + __ rsb(int_scratch, int_scratch, Operand(0), SetCC, mi); // Get mantisssa[51:20]. // Get the position of the first set bit. - __ CountLeadingZeros(dst1, scratch1, scratch2); + __ CountLeadingZeros(dst1, int_scratch, scratch2); __ rsb(dst1, dst1, Operand(31)); // Set the exponent. @@ -709,26 +570,52 @@ void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, // Clear the first non null bit. __ mov(scratch2, Operand(1)); - __ bic(scratch1, scratch1, Operand(scratch2, LSL, dst1)); + __ bic(int_scratch, int_scratch, Operand(scratch2, LSL, dst1)); __ cmp(dst1, Operand(HeapNumber::kMantissaBitsInTopWord)); // Get the number of bits to set in the lower part of the mantissa. __ sub(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC); __ b(mi, &fewer_than_20_useful_bits); // Set the higher 20 bits of the mantissa. - __ orr(dst2, dst2, Operand(scratch1, LSR, scratch2)); + __ orr(dst2, dst2, Operand(int_scratch, LSR, scratch2)); __ rsb(scratch2, scratch2, Operand(32)); - __ mov(dst1, Operand(scratch1, LSL, scratch2)); + __ mov(dst1, Operand(int_scratch, LSL, scratch2)); __ b(&done); __ bind(&fewer_than_20_useful_bits); __ rsb(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord)); - __ mov(scratch2, Operand(scratch1, LSL, scratch2)); + __ mov(scratch2, Operand(int_scratch, LSL, scratch2)); __ orr(dst2, dst2, scratch2); // Set dst1 to 0. __ mov(dst1, Operand(0)); } + __ bind(&done); +} + +void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, + Register object, + Destination destination, + DwVfpRegister double_dst, + Register dst1, + Register dst2, + Register heap_number_map, + Register scratch1, + Register scratch2, + SwVfpRegister single_scratch, + Label* not_int32) { + ASSERT(!scratch1.is(object) && !scratch2.is(object)); + ASSERT(!scratch1.is(scratch2)); + ASSERT(!heap_number_map.is(object) && + !heap_number_map.is(scratch1) && + !heap_number_map.is(scratch2)); + + Label done, obj_is_not_smi; + + __ JumpIfNotSmi(object, &obj_is_not_smi); + __ SmiUntag(scratch1, object); + ConvertIntToDouble(masm, scratch1, destination, double_dst, dst1, dst2, + scratch2, single_scratch); __ b(&done); __ bind(&obj_is_not_smi); diff --git a/src/arm/code-stubs-arm.h b/src/arm/code-stubs-arm.h index 0bb0025fc..b4d8f846e 100644 --- a/src/arm/code-stubs-arm.h +++ b/src/arm/code-stubs-arm.h @@ -368,6 +368,149 @@ class DirectCEntryStub: public CodeStub { }; +class FloatingPointHelper : public AllStatic { + public: + + enum Destination { + kVFPRegisters, + kCoreRegisters + }; + + + // Loads smis from r0 and r1 (right and left in binary operations) into + // floating point registers. Depending on the destination the values ends up + // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is + // floating point registers VFP3 must be supported. If core registers are + // requested when VFP3 is supported d6 and d7 will be scratched. + static void LoadSmis(MacroAssembler* masm, + Destination destination, + Register scratch1, + Register scratch2); + + // Loads objects from r0 and r1 (right and left in binary operations) into + // floating point registers. Depending on the destination the values ends up + // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is + // floating point registers VFP3 must be supported. If core registers are + // requested when VFP3 is supported d6 and d7 will still be scratched. If + // either r0 or r1 is not a number (not smi and not heap number object) the + // not_number label is jumped to with r0 and r1 intact. + static void LoadOperands(MacroAssembler* masm, + FloatingPointHelper::Destination destination, + Register heap_number_map, + Register scratch1, + Register scratch2, + Label* not_number); + + // Convert the smi or heap number in object to an int32 using the rules + // for ToInt32 as described in ECMAScript 9.5.: the value is truncated + // and brought into the range -2^31 .. +2^31 - 1. + static void ConvertNumberToInt32(MacroAssembler* masm, + Register object, + Register dst, + Register heap_number_map, + Register scratch1, + Register scratch2, + Register scratch3, + DwVfpRegister double_scratch, + Label* not_int32); + + // Converts the integer (untagged smi) in |int_scratch| to a double, storing + // the result either in |double_dst| or |dst2:dst1|, depending on + // |destination|. + // Warning: The value in |int_scratch| will be changed in the process! + static void ConvertIntToDouble(MacroAssembler* masm, + Register int_scratch, + Destination destination, + DwVfpRegister double_dst, + Register dst1, + Register dst2, + Register scratch2, + SwVfpRegister single_scratch); + + // Load the number from object into double_dst in the double format. + // Control will jump to not_int32 if the value cannot be exactly represented + // by a 32-bit integer. + // Floating point value in the 32-bit integer range that are not exact integer + // won't be loaded. + static void LoadNumberAsInt32Double(MacroAssembler* masm, + Register object, + Destination destination, + DwVfpRegister double_dst, + Register dst1, + Register dst2, + Register heap_number_map, + Register scratch1, + Register scratch2, + SwVfpRegister single_scratch, + Label* not_int32); + + // Loads the number from object into dst as a 32-bit integer. + // Control will jump to not_int32 if the object cannot be exactly represented + // by a 32-bit integer. + // Floating point value in the 32-bit integer range that are not exact integer + // won't be converted. + // scratch3 is not used when VFP3 is supported. + static void LoadNumberAsInt32(MacroAssembler* masm, + Register object, + Register dst, + Register heap_number_map, + Register scratch1, + Register scratch2, + Register scratch3, + DwVfpRegister double_scratch, + Label* not_int32); + + // Generate non VFP3 code to check if a double can be exactly represented by a + // 32-bit integer. This does not check for 0 or -0, which need + // to be checked for separately. + // Control jumps to not_int32 if the value is not a 32-bit integer, and falls + // through otherwise. + // src1 and src2 will be cloberred. + // + // Expected input: + // - src1: higher (exponent) part of the double value. + // - src2: lower (mantissa) part of the double value. + // Output status: + // - dst: 32 higher bits of the mantissa. (mantissa[51:20]) + // - src2: contains 1. + // - other registers are clobbered. + static void DoubleIs32BitInteger(MacroAssembler* masm, + Register src1, + Register src2, + Register dst, + Register scratch, + Label* not_int32); + + // Generates code to call a C function to do a double operation using core + // registers. (Used when VFP3 is not supported.) + // This code never falls through, but returns with a heap number containing + // the result in r0. + // Register heapnumber_result must be a heap number in which the + // result of the operation will be stored. + // Requires the following layout on entry: + // r0: Left value (least significant part of mantissa). + // r1: Left value (sign, exponent, top of mantissa). + // r2: Right value (least significant part of mantissa). + // r3: Right value (sign, exponent, top of mantissa). + static void CallCCodeForDoubleOperation(MacroAssembler* masm, + Token::Value op, + Register heap_number_result, + Register scratch); + + private: + static void LoadNumber(MacroAssembler* masm, + FloatingPointHelper::Destination destination, + Register object, + DwVfpRegister dst, + Register dst1, + Register dst2, + Register heap_number_map, + Register scratch1, + Register scratch2, + Label* not_number); +}; + + } } // namespace v8::internal #endif // V8_ARM_CODE_STUBS_ARM_H_ diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index b05e555e4..13ededb51 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1854,8 +1854,11 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { ExternalArrayType array_type = instr->array_type(); Representation representation(instr->representation()); - ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) || - (representation.IsDouble() && array_type == kExternalFloatArray)); + ASSERT( + (representation.IsInteger32() && (array_type != kExternalFloatArray && + array_type != kExternalDoubleArray)) || + (representation.IsDouble() && (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegister(instr->key()); @@ -1902,8 +1905,11 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { Representation representation(instr->value()->representation()); ExternalArrayType array_type = instr->array_type(); - ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) || - (representation.IsDouble() && array_type == kExternalFloatArray)); + ASSERT( + (representation.IsInteger32() && (array_type != kExternalFloatArray && + array_type != kExternalDoubleArray)) || + (representation.IsDouble() && (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray))); ASSERT(instr->external_pointer()->representation().IsExternal()); ASSERT(instr->key()->representation().IsInteger32()); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index c820e2e31..74fecc29d 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -2479,6 +2479,11 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( __ add(scratch0(), external_pointer, Operand(key, LSL, 2)); __ vldr(result.low(), scratch0(), 0); __ vcvt_f64_f32(result, result.low()); + } else if (array_type == kExternalDoubleArray) { + CpuFeatures::Scope scope(VFP3); + DwVfpRegister result(ToDoubleRegister(instr->result())); + __ add(scratch0(), external_pointer, Operand(key, LSL, 3)); + __ vldr(result, scratch0(), 0); } else { Register result(ToRegister(instr->result())); switch (array_type) { @@ -2507,6 +2512,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( DeoptimizeIf(cs, instr->environment()); break; case kExternalFloatArray: + case kExternalDoubleArray: UNREACHABLE(); break; } @@ -3232,12 +3238,18 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( Register external_pointer = ToRegister(instr->external_pointer()); Register key = ToRegister(instr->key()); ExternalArrayType array_type = instr->array_type(); + if (array_type == kExternalFloatArray) { CpuFeatures::Scope scope(VFP3); DwVfpRegister value(ToDoubleRegister(instr->value())); __ add(scratch0(), external_pointer, Operand(key, LSL, 2)); __ vcvt_f32_f64(double_scratch0().low(), value); __ vstr(double_scratch0().low(), scratch0(), 0); + } else if (array_type == kExternalDoubleArray) { + CpuFeatures::Scope scope(VFP3); + DwVfpRegister value(ToDoubleRegister(instr->value())); + __ add(scratch0(), external_pointer, Operand(key, LSL, 3)); + __ vstr(value, scratch0(), 0); } else { Register value(ToRegister(instr->value())); switch (array_type) { @@ -3259,6 +3271,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( __ str(value, MemOperand(external_pointer, key, LSL, 2)); break; case kExternalFloatArray: + case kExternalDoubleArray: UNREACHABLE(); break; } diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 47d675b07..97097c847 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -3517,6 +3517,18 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( __ ldr(value, MemOperand(r3, key, LSL, 1)); } break; + case kExternalDoubleArray: + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + __ add(r2, r3, Operand(key, LSL, 2)); + __ vldr(d0, r2, 0); + } else { + __ add(r4, r3, Operand(key, LSL, 2)); + // r4: pointer to the beginning of the double we want to load. + __ ldr(r2, MemOperand(r4, 0)); + __ ldr(r3, MemOperand(r4, Register::kSizeInBytes)); + } + break; default: UNREACHABLE(); break; @@ -3524,9 +3536,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( // For integer array types: // r2: value - // For floating-point array type + // For float array type: // s0: value (if VFP3 is supported) // r2: value (if VFP3 is not supported) + // For double array type: + // d0: value (if VFP3 is supported) + // r2/r3: value (if VFP3 is not supported) if (array_type == kExternalIntArray) { // For the Int and UnsignedInt array types, we need to see whether @@ -3694,6 +3709,31 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( __ mov(r0, r3); __ Ret(); } + } else if (array_type == kExternalDoubleArray) { + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + // Allocate a HeapNumber for the result. Don't use r0 and r1 as + // AllocateHeapNumber clobbers all registers - also when jumping due to + // exhausted young space. + __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(r2, r3, r4, r6, &slow); + __ sub(r1, r2, Operand(kHeapObjectTag)); + __ vstr(d0, r1, HeapNumber::kValueOffset); + + __ mov(r0, r2); + __ Ret(); + } else { + // Allocate a HeapNumber for the result. Don't use r0 and r1 as + // AllocateHeapNumber clobbers all registers - also when jumping due to + // exhausted young space. + __ LoadRoot(r7, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(r4, r5, r6, r7, &slow); + + __ str(r2, FieldMemOperand(r4, HeapNumber::kMantissaOffset)); + __ str(r3, FieldMemOperand(r4, HeapNumber::kExponentOffset)); + __ mov(r0, r4); + __ Ret(); + } } else { // Tag integer as smi and return it. @@ -3797,6 +3837,27 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( // Perform int-to-float conversion and store to memory. StoreIntAsFloat(masm(), r3, r4, r5, r6, r7, r9); break; + case kExternalDoubleArray: + __ add(r3, r3, Operand(r4, LSL, 3)); + // r3: effective address of the double element + FloatingPointHelper::Destination destination; + if (CpuFeatures::IsSupported(VFP3)) { + destination = FloatingPointHelper::kVFPRegisters; + } else { + destination = FloatingPointHelper::kCoreRegisters; + } + FloatingPointHelper::ConvertIntToDouble( + masm(), r5, destination, + d0, r6, r7, // These are: double_dst, dst1, dst2. + r4, s2); // These are: scratch2, single_scratch. + if (destination == FloatingPointHelper::kVFPRegisters) { + CpuFeatures::Scope scope(VFP3); + __ vstr(d0, r3, 0); + } else { + __ str(r6, MemOperand(r3, 0)); + __ str(r7, MemOperand(r3, Register::kSizeInBytes)); + } + break; default: UNREACHABLE(); break; @@ -3831,6 +3892,11 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ add(r5, r3, Operand(r4, LSL, 2)); __ vcvt_f32_f64(s0, d0); __ vstr(s0, r5, 0); + } else if (array_type == kExternalDoubleArray) { + __ sub(r5, r0, Operand(kHeapObjectTag)); + __ vldr(d0, r5, HeapNumber::kValueOffset); + __ add(r5, r3, Operand(r4, LSL, 3)); + __ vstr(d0, r5, 0); } else { // Need to perform float-to-int conversion. // Test for NaN or infinity (both give zero). @@ -3933,6 +3999,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ orr(r9, r9, Operand(r5, LSL, kMantissaInHiWordShift)); __ orr(r5, r9, Operand(r6, LSR, kMantissaInLoWordShift)); __ b(&done); + } else if (array_type == kExternalDoubleArray) { + __ add(r7, r3, Operand(r4, LSL, 3)); + // r7: effective address of destination element. + __ str(r6, MemOperand(r7, 0)); + __ str(r5, MemOperand(r7, Register::kSizeInBytes)); + __ Ret(); } else { bool is_signed_type = IsElementTypeSigned(array_type); int meaningfull_bits = is_signed_type ? (kBitsPerInt - 1) : kBitsPerInt; diff --git a/src/heap.cc b/src/heap.cc index 9a3cfe410..518f4ab4d 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1800,6 +1800,12 @@ bool Heap::CreateInitialMaps() { } set_external_float_array_map(Map::cast(obj)); + { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_DOUBLE_ARRAY_TYPE, + ExternalArray::kAlignedSize); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_external_double_array_map(Map::cast(obj)); + { MaybeObject* maybe_obj = AllocateMap(CODE_TYPE, kVariableSizeSentinel); if (!maybe_obj->ToObject(&obj)) return false; } @@ -2288,6 +2294,8 @@ Heap::RootListIndex Heap::RootIndexForExternalArrayType( return kExternalUnsignedIntArrayMapRootIndex; case kExternalFloatArray: return kExternalFloatArrayMapRootIndex; + case kExternalDoubleArray: + return kExternalDoubleArrayMapRootIndex; case kExternalPixelArray: return kExternalPixelArrayMapRootIndex; default: diff --git a/src/heap.h b/src/heap.h index 7a1bed356..d0c1551aa 100644 --- a/src/heap.h +++ b/src/heap.h @@ -103,6 +103,7 @@ inline Heap* _inline_get_heap_(); V(Map, external_int_array_map, ExternalIntArrayMap) \ V(Map, external_unsigned_int_array_map, ExternalUnsignedIntArrayMap) \ V(Map, external_float_array_map, ExternalFloatArrayMap) \ + V(Map, external_double_array_map, ExternalDoubleArrayMap) \ V(Map, context_map, ContextMap) \ V(Map, catch_context_map, CatchContextMap) \ V(Map, code_map, CodeMap) \ @@ -217,6 +218,7 @@ inline Heap* _inline_get_heap_(); V(KeyedLoadExternalUnsignedIntArray_symbol, \ "KeyedLoadExternalUnsignedIntArray") \ V(KeyedLoadExternalFloatArray_symbol, "KeyedLoadExternalFloatArray") \ + V(KeyedLoadExternalDoubleArray_symbol, "KeyedLoadExternalDoubleArray") \ V(KeyedLoadExternalPixelArray_symbol, "KeyedLoadExternalPixelArray") \ V(KeyedStoreExternalByteArray_symbol, "KeyedStoreExternalByteArray") \ V(KeyedStoreExternalUnsignedByteArray_symbol, \ @@ -228,6 +230,7 @@ inline Heap* _inline_get_heap_(); V(KeyedStoreExternalUnsignedIntArray_symbol, \ "KeyedStoreExternalUnsignedIntArray") \ V(KeyedStoreExternalFloatArray_symbol, "KeyedStoreExternalFloatArray") \ + V(KeyedStoreExternalDoubleArray_symbol, "KeyedStoreExternalDoubleArray") \ V(KeyedStoreExternalPixelArray_symbol, "KeyedStoreExternalPixelArray") // Forward declarations. diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index b50b00b36..3aef7f2d1 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1287,6 +1287,9 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo( case kExternalFloatArray: stream->Add("float"); break; + case kExternalDoubleArray: + stream->Add("double"); + break; case kExternalPixelArray: stream->Add("pixel"); break; @@ -1364,6 +1367,9 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( case kExternalFloatArray: stream->Add("float"); break; + case kExternalDoubleArray: + stream->Add("double"); + break; case kExternalPixelArray: stream->Add("pixel"); break; diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 8d39b6933..b7d5d6b61 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -3246,7 +3246,8 @@ class HLoadKeyedSpecializedArrayElement: public HBinaryOperation { ExternalArrayType array_type) : HBinaryOperation(external_elements, key), array_type_(array_type) { - if (array_type == kExternalFloatArray) { + if (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray) { set_representation(Representation::Double()); } else { set_representation(Representation::Integer32()); @@ -3438,7 +3439,8 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> { if (index == 0) { return Representation::External(); } else { - if (index == 2 && array_type() == kExternalFloatArray) { + if (index == 2 && (array_type() == kExternalFloatArray || + array_type() == kExternalDoubleArray)) { return Representation::Double(); } else { return Representation::Integer32(); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 46c71e8b1..423cb69c7 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2338,6 +2338,9 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( XMMRegister result(ToDoubleRegister(instr->result())); __ movss(result, Operand(external_pointer, key, times_4, 0)); __ cvtss2sd(result, result); + } else if (array_type == kExternalDoubleArray) { + __ movdbl(ToDoubleRegister(instr->result()), + Operand(external_pointer, key, times_8, 0)); } else { Register result(ToRegister(instr->result())); switch (array_type) { @@ -2366,6 +2369,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( DeoptimizeIf(negative, instr->environment()); break; case kExternalFloatArray: + case kExternalDoubleArray: UNREACHABLE(); break; } @@ -3047,6 +3051,9 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( if (array_type == kExternalFloatArray) { __ cvtsd2ss(xmm0, ToDoubleRegister(instr->value())); __ movss(Operand(external_pointer, key, times_4, 0), xmm0); + } else if (array_type == kExternalDoubleArray) { + __ movdbl(Operand(external_pointer, key, times_8, 0), + ToDoubleRegister(instr->value())); } else { Register value = ToRegister(instr->value()); switch (array_type) { @@ -3081,6 +3088,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( __ mov(Operand(external_pointer, key, times_4, 0), value); break; case kExternalFloatArray: + case kExternalDoubleArray: UNREACHABLE(); break; } diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 735abe624..0d64639df 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1888,8 +1888,11 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { ExternalArrayType array_type = instr->array_type(); Representation representation(instr->representation()); - ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) || - (representation.IsDouble() && array_type == kExternalFloatArray)); + ASSERT( + (representation.IsInteger32() && (array_type != kExternalFloatArray && + array_type != kExternalDoubleArray)) || + (representation.IsDouble() && (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegister(instr->key()); @@ -1938,8 +1941,11 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { Representation representation(instr->value()->representation()); ExternalArrayType array_type = instr->array_type(); - ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) || - (representation.IsDouble() && array_type == kExternalFloatArray)); + ASSERT( + (representation.IsInteger32() && (array_type != kExternalFloatArray && + array_type != kExternalDoubleArray)) || + (representation.IsDouble() && (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray))); ASSERT(instr->external_pointer()->representation().IsExternal()); ASSERT(instr->key()->representation().IsInteger32()); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 27d28868f..c6251157f 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -3397,6 +3397,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( case kExternalFloatArray: __ fld_s(Operand(ebx, ecx, times_4, 0)); break; + case kExternalDoubleArray: + __ fld_d(Operand(ebx, ecx, times_8, 0)); + break; default: UNREACHABLE(); break; @@ -3454,7 +3457,8 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( __ mov(eax, ecx); __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ ret(0); - } else if (array_type == kExternalFloatArray) { + } else if (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray) { // For the floating-point array type, we need to always allocate a // HeapNumber. __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation); @@ -3569,11 +3573,16 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ mov(Operand(edi, ebx, times_4, 0), ecx); break; case kExternalFloatArray: + case kExternalDoubleArray: // Need to perform int-to-float conversion. __ push(ecx); __ fild_s(Operand(esp, 0)); __ pop(ecx); - __ fstp_s(Operand(edi, ebx, times_4, 0)); + if (array_type == kExternalFloatArray) { + __ fstp_s(Operand(edi, ebx, times_4, 0)); + } else { // array_type == kExternalDoubleArray. + __ fstp_d(Operand(edi, ebx, times_8, 0)); + } break; default: UNREACHABLE(); @@ -3603,6 +3612,10 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ fstp_s(Operand(edi, ebx, times_4, 0)); __ ret(0); + } else if (array_type == kExternalDoubleArray) { + __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); + __ fstp_d(Operand(edi, ebx, times_8, 0)); + __ ret(0); } else { // Perform float-to-int conversion with truncation (round-to-zero) // behavior. diff --git a/src/objects-debug.cc b/src/objects-debug.cc index dd606dcd0..a0ce6050e 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -116,6 +116,9 @@ void HeapObject::HeapObjectVerify() { case EXTERNAL_FLOAT_ARRAY_TYPE: ExternalFloatArray::cast(this)->ExternalFloatArrayVerify(); break; + case EXTERNAL_DOUBLE_ARRAY_TYPE: + ExternalDoubleArray::cast(this)->ExternalDoubleArrayVerify(); + break; case CODE_TYPE: Code::cast(this)->CodeVerify(); break; @@ -232,6 +235,11 @@ void ExternalFloatArray::ExternalFloatArrayVerify() { } +void ExternalDoubleArray::ExternalDoubleArrayVerify() { + ASSERT(IsExternalDoubleArray()); +} + + void JSObject::JSObjectVerify() { VerifyHeapPointer(properties()); VerifyHeapPointer(elements()); diff --git a/src/objects-inl.h b/src/objects-inl.h index 823b2dadc..2ed658c7e 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -406,6 +406,13 @@ bool Object::IsExternalFloatArray() { } +bool Object::IsExternalDoubleArray() { + return Object::IsHeapObject() && + HeapObject::cast(this)->map()->instance_type() == + EXTERNAL_DOUBLE_ARRAY_TYPE; +} + + bool MaybeObject::IsFailure() { return HAS_FAILURE_TAG(this); } @@ -1901,6 +1908,7 @@ CAST_ACCESSOR(ExternalUnsignedShortArray) CAST_ACCESSOR(ExternalIntArray) CAST_ACCESSOR(ExternalUnsignedIntArray) CAST_ACCESSOR(ExternalFloatArray) +CAST_ACCESSOR(ExternalDoubleArray) CAST_ACCESSOR(ExternalPixelArray) CAST_ACCESSOR(Struct) @@ -2315,6 +2323,20 @@ void ExternalFloatArray::set(int index, float value) { } +double ExternalDoubleArray::get(int index) { + ASSERT((index >= 0) && (index < this->length())); + double* ptr = static_cast(external_pointer()); + return ptr[index]; +} + + +void ExternalDoubleArray::set(int index, double value) { + ASSERT((index >= 0) && (index < this->length())); + double* ptr = static_cast(external_pointer()); + ptr[index] = value; +} + + int Map::visitor_id() { return READ_BYTE_FIELD(this, kVisitorIdOffset); } @@ -3638,14 +3660,18 @@ JSObject::ElementsKind JSObject::GetElementsKind() { return EXTERNAL_INT_ELEMENTS; case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: return EXTERNAL_UNSIGNED_INT_ELEMENTS; + case EXTERNAL_FLOAT_ARRAY_TYPE: + return EXTERNAL_FLOAT_ELEMENTS; + case EXTERNAL_DOUBLE_ARRAY_TYPE: + return EXTERNAL_DOUBLE_ELEMENTS; case EXTERNAL_PIXEL_ARRAY_TYPE: return EXTERNAL_PIXEL_ELEMENTS; default: break; } } - ASSERT(array->map()->instance_type() == EXTERNAL_FLOAT_ARRAY_TYPE); - return EXTERNAL_FLOAT_ELEMENTS; + UNREACHABLE(); + return DICTIONARY_ELEMENTS; } @@ -3686,6 +3712,8 @@ EXTERNAL_ELEMENTS_CHECK(UnsignedInt, EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) EXTERNAL_ELEMENTS_CHECK(Float, EXTERNAL_FLOAT_ARRAY_TYPE) +EXTERNAL_ELEMENTS_CHECK(Double, + EXTERNAL_DOUBLE_ARRAY_TYPE) EXTERNAL_ELEMENTS_CHECK(Pixel, EXTERNAL_PIXEL_ARRAY_TYPE) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index b7e2fdd8f..ac5e2045f 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -114,6 +114,9 @@ void HeapObject::HeapObjectPrint(FILE* out) { case EXTERNAL_FLOAT_ARRAY_TYPE: ExternalFloatArray::cast(this)->ExternalFloatArrayPrint(out); break; + case EXTERNAL_DOUBLE_ARRAY_TYPE: + ExternalDoubleArray::cast(this)->ExternalDoubleArrayPrint(out); + break; case FILLER_TYPE: PrintF(out, "filler"); break; @@ -217,6 +220,11 @@ void ExternalFloatArray::ExternalFloatArrayPrint(FILE* out) { } +void ExternalDoubleArray::ExternalDoubleArrayPrint(FILE* out) { + PrintF(out, "external double array"); +} + + void JSObject::PrintProperties(FILE* out) { if (HasFastProperties()) { DescriptorArray* descs = map()->instance_descriptors(); @@ -330,6 +338,13 @@ void JSObject::PrintElements(FILE* out) { } break; } + case EXTERNAL_DOUBLE_ELEMENTS: { + ExternalDoubleArray* p = ExternalDoubleArray::cast(elements()); + for (int i = 0; i < p->length(); i++) { + PrintF(out, " %d: %f\n", i, p->get(i)); + } + break; + } case DICTIONARY_ELEMENTS: elements()->Print(out); break; @@ -383,6 +398,7 @@ static const char* TypeToString(InstanceType type) { case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: return "EXTERNAL_UNSIGNED_INT_ARRAY"; case EXTERNAL_FLOAT_ARRAY_TYPE: return "EXTERNAL_FLOAT_ARRAY"; + case EXTERNAL_DOUBLE_ARRAY_TYPE: return "EXTERNAL_DOUBLE_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-visiting.cc b/src/objects-visiting.cc index 5a23658cb..bcb48ea5a 100644 --- a/src/objects-visiting.cc +++ b/src/objects-visiting.cc @@ -121,6 +121,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case EXTERNAL_INT_ARRAY_TYPE: case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: case EXTERNAL_FLOAT_ARRAY_TYPE: + case EXTERNAL_DOUBLE_ARRAY_TYPE: return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric, instance_size); diff --git a/src/objects.cc b/src/objects.cc index 6ce4c4417..c3a900d0d 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1045,6 +1045,10 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { accumulator->Add("", ExternalFloatArray::cast(this)->length()); break; + case EXTERNAL_DOUBLE_ARRAY_TYPE: + accumulator->Add("", + ExternalDoubleArray::cast(this)->length()); + break; case SHARED_FUNCTION_INFO_TYPE: accumulator->Add(""); break; @@ -1174,6 +1178,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case EXTERNAL_INT_ARRAY_TYPE: case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: case EXTERNAL_FLOAT_ARRAY_TYPE: + case EXTERNAL_DOUBLE_ARRAY_TYPE: break; case SHARED_FUNCTION_INFO_TYPE: SharedFunctionInfo::BodyDescriptor::IterateBody(this, v); @@ -2848,6 +2853,7 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: // Pixel and external array elements cannot be deleted. Just // silently ignore here. break; @@ -2967,6 +2973,7 @@ bool JSObject::ReferencesObject(Object* obj) { case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: // Raw pixels and external arrays do not reference other // objects. break; @@ -3238,6 +3245,7 @@ MaybeObject* JSObject::DefineGetterSetter(String* name, case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: // Ignore getters and setters on pixel and external array // elements. return heap->undefined_value(); @@ -3464,6 +3472,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: // Ignore getters and setters on pixel and external array // elements. return isolate->heap()->undefined_value(); @@ -6963,7 +6972,8 @@ bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) { case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { ExternalArray* array = ExternalArray::cast(elements()); if (index < static_cast(array->length())) { return true; @@ -7085,7 +7095,8 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { ExternalArray* array = ExternalArray::cast(elements()); if (index < static_cast(array->length())) return FAST_ELEMENT; break; @@ -7144,7 +7155,8 @@ bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) { case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { ExternalArray* array = ExternalArray::cast(elements()); if (index < static_cast(array->length())) { return true; @@ -7465,6 +7477,10 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, ExternalFloatArray* array = ExternalFloatArray::cast(elements()); return array->SetValue(index, value); } + case EXTERNAL_DOUBLE_ELEMENTS: { + ExternalDoubleArray* array = ExternalDoubleArray::cast(elements()); + return array->SetValue(index, value); + } case DICTIONARY_ELEMENTS: { // Insert element in the dictionary. FixedArray* elms = FixedArray::cast(elements()); @@ -7606,7 +7622,8 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver, case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { MaybeObject* maybe_value = GetExternalElement(index); Object* value; if (!maybe_value->ToObject(&value)) return maybe_value; @@ -7708,7 +7725,8 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver, case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { MaybeObject* maybe_value = GetExternalElement(index); Object* value; if (!maybe_value->ToObject(&value)) return maybe_value; @@ -7811,6 +7829,14 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) { } break; } + case EXTERNAL_DOUBLE_ELEMENTS: { + ExternalDoubleArray* array = ExternalDoubleArray::cast(elements()); + if (index < static_cast(array->length())) { + double value = array->get(index); + return GetHeap()->AllocateHeapNumber(value); + } + break; + } case FAST_ELEMENTS: case DICTIONARY_ELEMENTS: UNREACHABLE(); @@ -7840,7 +7866,8 @@ bool JSObject::HasDenseElements() { case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { return true; } case DICTIONARY_ELEMENTS: { @@ -8077,7 +8104,8 @@ bool JSObject::HasRealElementProperty(uint32_t index) { case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { ExternalArray* array = ExternalArray::cast(elements()); return index < static_cast(array->length()); } @@ -8318,7 +8346,8 @@ int JSObject::GetLocalElementKeys(FixedArray* storage, case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: case EXTERNAL_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: { int length = ExternalArray::cast(elements())->length(); while (counter < length) { if (storage != NULL) { @@ -9233,6 +9262,26 @@ MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) { } +MaybeObject* ExternalDoubleArray::SetValue(uint32_t index, Object* value) { + double double_value = 0; + Heap* heap = GetHeap(); + if (index < static_cast(length())) { + if (value->IsSmi()) { + int int_value = Smi::cast(value)->value(); + double_value = static_cast(int_value); + } else if (value->IsHeapNumber()) { + double_value = HeapNumber::cast(value)->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, double_value); + } + return heap->AllocateHeapNumber(double_value); +} + + JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) { ASSERT(!HasFastProperties()); Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry()); diff --git a/src/objects.h b/src/objects.h index 03445e874..fc89b578b 100644 --- a/src/objects.h +++ b/src/objects.h @@ -523,6 +523,7 @@ enum InstanceType { EXTERNAL_INT_ARRAY_TYPE, EXTERNAL_UNSIGNED_INT_ARRAY_TYPE, EXTERNAL_FLOAT_ARRAY_TYPE, + EXTERNAL_DOUBLE_ARRAY_TYPE, EXTERNAL_PIXEL_ARRAY_TYPE, // LAST_EXTERNAL_ARRAY_TYPE FILLER_TYPE, // LAST_DATA_TYPE @@ -691,6 +692,7 @@ class MaybeObject BASE_EMBEDDED { V(ExternalIntArray) \ V(ExternalUnsignedIntArray) \ V(ExternalFloatArray) \ + V(ExternalDoubleArray) \ V(ExternalPixelArray) \ V(ByteArray) \ V(JSObject) \ @@ -1336,6 +1338,7 @@ class JSObject: public HeapObject { EXTERNAL_INT_ELEMENTS, EXTERNAL_UNSIGNED_INT_ELEMENTS, EXTERNAL_FLOAT_ELEMENTS, + EXTERNAL_DOUBLE_ELEMENTS, EXTERNAL_PIXEL_ELEMENTS }; @@ -1377,6 +1380,7 @@ class JSObject: public HeapObject { inline bool HasExternalIntElements(); inline bool HasExternalUnsignedIntElements(); inline bool HasExternalFloatElements(); + inline bool HasExternalDoubleElements(); inline bool AllowsSetElementsLength(); inline NumberDictionary* element_dictionary(); // Gets slow elements. // Requires: this->HasFastElements(). @@ -3093,6 +3097,34 @@ class ExternalFloatArray: public ExternalArray { }; +class ExternalDoubleArray: public ExternalArray { + public: + // Setter and getter. + inline double get(int index); + inline void set(int index, double value); + + // This accessor applies the correct conversion from Smi, HeapNumber + // and undefined. + MaybeObject* SetValue(uint32_t index, Object* value); + + // Casting. + static inline ExternalDoubleArray* cast(Object* obj); + +#ifdef OBJECT_PRINT + inline void ExternalDoubleArrayPrint() { + ExternalDoubleArrayPrint(stdout); + } + void ExternalDoubleArrayPrint(FILE* out); +#endif // OBJECT_PRINT +#ifdef DEBUG + void ExternalDoubleArrayVerify(); +#endif // DEBUG + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalDoubleArray); +}; + + // DeoptimizationInputData is a fixed array used to hold the deoptimization // data for code generated by the Hydrogen/Lithium compiler. It also // contains information about functions that were inlined. If N different diff --git a/src/runtime.cc b/src/runtime.cc index 53c048e9a..ae2693efd 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -8603,43 +8603,48 @@ static void CollectElementIndices(Handle object, int dense_elements_length; switch (kind) { case JSObject::EXTERNAL_PIXEL_ELEMENTS: { - dense_elements_length = - ExternalPixelArray::cast(object->elements())->length(); + dense_elements_length = + ExternalPixelArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_BYTE_ELEMENTS: { - dense_elements_length = - ExternalByteArray::cast(object->elements())->length(); + dense_elements_length = + ExternalByteArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { - dense_elements_length = - ExternalUnsignedByteArray::cast(object->elements())->length(); + dense_elements_length = + ExternalUnsignedByteArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_SHORT_ELEMENTS: { - dense_elements_length = - ExternalShortArray::cast(object->elements())->length(); + dense_elements_length = + ExternalShortArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { - dense_elements_length = - ExternalUnsignedShortArray::cast(object->elements())->length(); + dense_elements_length = + ExternalUnsignedShortArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_INT_ELEMENTS: { - dense_elements_length = - ExternalIntArray::cast(object->elements())->length(); + dense_elements_length = + ExternalIntArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { - dense_elements_length = - ExternalUnsignedIntArray::cast(object->elements())->length(); + dense_elements_length = + ExternalUnsignedIntArray::cast(object->elements())->length(); break; } case JSObject::EXTERNAL_FLOAT_ELEMENTS: { - dense_elements_length = - ExternalFloatArray::cast(object->elements())->length(); + dense_elements_length = + ExternalFloatArray::cast(object->elements())->length(); + break; + } + case JSObject::EXTERNAL_DOUBLE_ELEMENTS: { + dense_elements_length = + ExternalDoubleArray::cast(object->elements())->length(); break; } default: @@ -8773,6 +8778,11 @@ static bool IterateElements(Isolate* isolate, isolate, receiver, false, false, visitor); break; } + case JSObject::EXTERNAL_DOUBLE_ELEMENTS: { + IterateExternalArrayElements( + isolate, receiver, false, false, visitor); + break; + } default: UNREACHABLE(); break; diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 0c6a7f743..7a1b185df 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -555,6 +555,8 @@ ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) { return kExternalUnsignedIntArray; case JSObject::EXTERNAL_FLOAT_ELEMENTS: return kExternalFloatArray; + case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + return kExternalDoubleArray; case JSObject::EXTERNAL_PIXEL_ELEMENTS: return kExternalPixelArray; default: @@ -582,6 +584,8 @@ String* ExternalArrayTypeToStubName(Heap* heap, return heap->KeyedStoreExternalUnsignedIntArray_symbol(); case kExternalFloatArray: return heap->KeyedStoreExternalFloatArray_symbol(); + case kExternalDoubleArray: + return heap->KeyedStoreExternalDoubleArray_symbol(); case kExternalPixelArray: return heap->KeyedStoreExternalPixelArray_symbol(); default: @@ -604,6 +608,8 @@ String* ExternalArrayTypeToStubName(Heap* heap, return heap->KeyedLoadExternalUnsignedIntArray_symbol(); case kExternalFloatArray: return heap->KeyedLoadExternalFloatArray_symbol(); + case kExternalDoubleArray: + return heap->KeyedLoadExternalDoubleArray_symbol(); case kExternalPixelArray: return heap->KeyedLoadExternalPixelArray_symbol(); default: diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index c242874c5..688a557ee 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2350,6 +2350,9 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( XMMRegister result(ToDoubleRegister(instr->result())); __ movss(result, Operand(external_pointer, key, times_4, 0)); __ cvtss2sd(result, result); + } else if (array_type == kExternalDoubleArray) { + __ movsd(ToDoubleRegister(instr->result()), + Operand(external_pointer, key, times_8, 0)); } else { Register result(ToRegister(instr->result())); switch (array_type) { @@ -2378,6 +2381,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( DeoptimizeIf(negative, instr->environment()); break; case kExternalFloatArray: + case kExternalDoubleArray: UNREACHABLE(); break; } @@ -3037,6 +3041,9 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( XMMRegister value(ToDoubleRegister(instr->value())); __ cvtsd2ss(value, value); __ movss(Operand(external_pointer, key, times_4, 0), value); + } else if (array_type == kExternalDoubleArray) { + __ movsd(Operand(external_pointer, key, times_8, 0), + ToDoubleRegister(instr->value())); } else { Register value(ToRegister(instr->value())); switch (array_type) { @@ -3064,6 +3071,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( __ movl(Operand(external_pointer, key, times_4, 0), value); break; case kExternalFloatArray: + case kExternalDoubleArray: UNREACHABLE(); break; } diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 931424ee0..0c36934b5 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1844,8 +1844,11 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { ExternalArrayType array_type = instr->array_type(); Representation representation(instr->representation()); - ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) || - (representation.IsDouble() && array_type == kExternalFloatArray)); + ASSERT( + (representation.IsInteger32() && (array_type != kExternalFloatArray && + array_type != kExternalDoubleArray)) || + (representation.IsDouble() && (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegister(instr->key()); @@ -1891,8 +1894,11 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { Representation representation(instr->value()->representation()); ExternalArrayType array_type = instr->array_type(); - ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) || - (representation.IsDouble() && array_type == kExternalFloatArray)); + ASSERT( + (representation.IsInteger32() && (array_type != kExternalFloatArray && + array_type != kExternalDoubleArray)) || + (representation.IsDouble() && (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray))); ASSERT(instr->external_pointer()->representation().IsExternal()); ASSERT(instr->key()->representation().IsInteger32()); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index c19d29d0d..ef6f75838 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -3214,6 +3214,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( case kExternalFloatArray: __ cvtss2sd(xmm0, Operand(rbx, rcx, times_4, 0)); break; + case kExternalDoubleArray: + __ movsd(xmm0, Operand(rbx, rcx, times_8, 0)); + break; default: UNREACHABLE(); break; @@ -3251,7 +3254,8 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub( __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); __ movq(rax, rcx); __ ret(0); - } else if (array_type == kExternalFloatArray) { + } else if (array_type == kExternalFloatArray || + array_type == kExternalDoubleArray) { // For the floating-point array type, we need to always allocate a // HeapNumber. __ AllocateHeapNumber(rcx, rbx, &slow); @@ -3361,6 +3365,11 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ cvtlsi2ss(xmm0, rdx); __ movss(Operand(rbx, rdi, times_4, 0), xmm0); break; + case kExternalDoubleArray: + // Need to perform int-to-float conversion. + __ cvtlsi2sd(xmm0, rdx); + __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); + break; default: UNREACHABLE(); break; @@ -3391,6 +3400,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ cvtsd2ss(xmm0, xmm0); __ movss(Operand(rbx, rdi, times_4, 0), xmm0); __ ret(0); + } else if (array_type == kExternalDoubleArray) { + __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); + __ ret(0); } else { // Perform float-to-int conversion with truncation (round-to-zero) // behavior. diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 28a9be159..19492a532 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -11380,6 +11380,9 @@ static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { case v8::kExternalFloatArray: return 4; break; + case v8::kExternalDoubleArray: + return 8; + break; default: UNREACHABLE(); return -1; @@ -11569,7 +11572,8 @@ static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, CHECK_EQ( 2, static_cast(jsobj->GetElement(6)->ToObjectChecked()->Number())); - if (array_type != v8::kExternalFloatArray) { + if (array_type != v8::kExternalFloatArray && + array_type != v8::kExternalDoubleArray) { // Though the specification doesn't state it, be explicit about // converting NaNs and +/-Infinity to zero. result = CompileRun("for (var i = 0; i < 8; i++) {" @@ -11966,6 +11970,14 @@ THREADED_TEST(ExternalFloatArray) { } +THREADED_TEST(ExternalDoubleArray) { + ExternalArrayTestHelper( + v8::kExternalDoubleArray, + -500, + 500); +} + + THREADED_TEST(ExternalArrays) { TestExternalByteArray(); TestExternalUnsignedByteArray(); @@ -12003,6 +12015,7 @@ THREADED_TEST(ExternalArrayInfo) { ExternalArrayInfoTestHelper(v8::kExternalIntArray); ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); ExternalArrayInfoTestHelper(v8::kExternalFloatArray); + ExternalArrayInfoTestHelper(v8::kExternalDoubleArray); ExternalArrayInfoTestHelper(v8::kExternalPixelArray); } diff --git a/test/mjsunit/external-array.js b/test/mjsunit/external-array.js index 45d8be5a9..71f9b84e9 100644 --- a/test/mjsunit/external-array.js +++ b/test/mjsunit/external-array.js @@ -63,3 +63,28 @@ assertEquals(1, a.BYTES_PER_ELEMENT); a = new Int16Array(2); assertEquals(2, a.BYTES_PER_ELEMENT); +// Test Float64Arrays. +function get(a, index) { + return a[index]; +} +function set(a, index, value) { + a[index] = value; +} + +var array = new Float64Array(2); +for (var i = 0; i < 5; i++) { + set(array, 0, 2.5); + assertEquals(2.5, array[0]); +} +%OptimizeFunctionOnNextCall(set); +set(array, 0, 2.5); +assertEquals(2.5, array[0]); +set(array, 1, 3.5); +assertEquals(3.5, array[1]); +for (var i = 0; i < 5; i++) { + assertEquals(2.5, get(array, 0)); + assertEquals(3.5, array[1]); +} +%OptimizeFunctionOnNextCall(get); +assertEquals(2.5, get(array, 0)); +assertEquals(3.5, get(array, 1));