From: danno@chromium.org Date: Thu, 21 Nov 2013 17:21:00 +0000 (+0000) Subject: Revert 17963, 17962 and 17955: Random number generator in JS changes X-Git-Tag: upstream/4.7.83~11609 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8e266c22447dc78a26cfcfb0d51d18961dcd76de;p=platform%2Fupstream%2Fv8.git Revert 17963, 17962 and 17955: Random number generator in JS changes Revert 17966, 17965 also as collateral damage: Embed trigonometric lookup table. Due to Heapcheck and valgrind failures that are not yet fixed. TBR=svenpanne@chromium.org Review URL: https://codereview.chromium.org/80513004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17981 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 4a39970..64a9fdf 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -3347,6 +3347,50 @@ void FullCodeGenerator::EmitLog(CallRuntime* expr) { } +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); + Label slow_allocate_heapnumber; + Label heapnumber_allocated; + + __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber); + __ jmp(&heapnumber_allocated); + + __ bind(&slow_allocate_heapnumber); + // Allocate a heap number. + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(r4, Operand(r0)); + + __ bind(&heapnumber_allocated); + + // Convert 32 random bits in r0 to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + __ PrepareCallCFunction(1, r0); + __ ldr(r0, + ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX)); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kNativeContextOffset)); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); + + // 0x41300000 is the top half of 1.0 x 2^20 as a double. + // Create this constant using mov/orr to avoid PC relative load. + __ mov(r1, Operand(0x41000000)); + __ orr(r1, r1, Operand(0x300000)); + // Move 0x41300000xxxxxxxx (x = random bits) to VFP. + __ vmov(d7, r0, r1); + // Move 0x4130000000000000 to VFP. + __ mov(r0, Operand::Zero()); + __ vmov(d8, r0, r1); + // Subtract and store the result in the heap number. + __ vsub(d7, d7, d8); + __ sub(r0, r4, Operand(kHeapObjectTag)); + __ vstr(d7, r0, HeapNumber::kValueOffset); + __ mov(r0, r4); + + context()->Plug(r0); +} + + void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index eeffc16..a1e892b 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1720,6 +1720,19 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseTempRegister(instr->global_object()); + LOperand* scratch = TempRegister(); + LOperand* scratch2 = TempRegister(); + LOperand* scratch3 = TempRegister(); + LRandom* result = new(zone()) LRandom( + global_object, scratch, scratch2, scratch3); + return DefineFixedDouble(result, d7); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { ASSERT(instr->left()->representation().IsTagged()); ASSERT(instr->right()->representation().IsTagged()); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index 72932de..5b9278b 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -155,6 +155,7 @@ class LCodeGen; V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(SeqStringGetChar) \ @@ -1480,6 +1481,28 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> { }; +class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> { + public: + LRandom(LOperand* global_object, + LOperand* scratch, + LOperand* scratch2, + LOperand* scratch3) { + inputs_[0] = global_object; + temps_[0] = scratch; + temps_[1] = scratch2; + temps_[2] = scratch3; + } + + LOperand* global_object() const { return inputs_[0]; } + LOperand* scratch() const { return temps_[0]; } + LOperand* scratch2() const { return temps_[1]; } + LOperand* scratch3() const { return temps_[2]; } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD V8_FINAL : public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index cd9c712..0aa8197 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -3934,6 +3934,68 @@ void LCodeGen::DoPower(LPower* instr) { } +void LCodeGen::DoRandom(LRandom* instr) { + // Assert that the register size is indeed the size of each seed. + static const int kSeedSize = sizeof(uint32_t); + STATIC_ASSERT(kPointerSize == kSeedSize); + + // Load native context + Register global_object = ToRegister(instr->global_object()); + Register native_context = global_object; + __ ldr(native_context, FieldMemOperand( + global_object, GlobalObject::kNativeContextOffset)); + + // Load state (FixedArray of the native context's random seeds) + static const int kRandomSeedOffset = + FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize; + Register state = native_context; + __ ldr(state, FieldMemOperand(native_context, kRandomSeedOffset)); + + // Load state[0]. + Register state0 = ToRegister(instr->scratch()); + __ ldr(state0, FieldMemOperand(state, ByteArray::kHeaderSize)); + // Load state[1]. + Register state1 = ToRegister(instr->scratch2()); + __ ldr(state1, FieldMemOperand(state, ByteArray::kHeaderSize + kSeedSize)); + + // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16) + Register scratch3 = ToRegister(instr->scratch3()); + Register scratch4 = scratch0(); + __ and_(scratch3, state0, Operand(0xFFFF)); + __ mov(scratch4, Operand(18273)); + __ mul(scratch3, scratch3, scratch4); + __ add(state0, scratch3, Operand(state0, LSR, 16)); + // Save state[0]. + __ str(state0, FieldMemOperand(state, ByteArray::kHeaderSize)); + + // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16) + __ and_(scratch3, state1, Operand(0xFFFF)); + __ mov(scratch4, Operand(36969)); + __ mul(scratch3, scratch3, scratch4); + __ add(state1, scratch3, Operand(state1, LSR, 16)); + // Save state[1]. + __ str(state1, FieldMemOperand(state, ByteArray::kHeaderSize + kSeedSize)); + + // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF) + Register random = scratch4; + __ and_(random, state1, Operand(0x3FFFF)); + __ add(random, random, Operand(state0, LSL, 14)); + + // 0x41300000 is the top half of 1.0 x 2^20 as a double. + // Create this constant using mov/orr to avoid PC relative load. + __ mov(scratch3, Operand(0x41000000)); + __ orr(scratch3, scratch3, Operand(0x300000)); + // Move 0x41300000xxxxxxxx (x = random bits) to VFP. + DwVfpRegister result = ToDoubleRegister(instr->result()); + __ vmov(result, random, scratch3); + // Move 0x4130000000000000 to VFP. + __ mov(scratch4, Operand::Zero()); + DwVfpRegister scratch5 = double_scratch0(); + __ vmov(scratch5, scratch4, scratch3); + __ vsub(result, result, scratch5); +} + + void LCodeGen::DoMathExp(LMathExp* instr) { DwVfpRegister input = ToDoubleRegister(instr->value()); DwVfpRegister result = ToDoubleRegister(instr->result()); diff --git a/src/assembler.cc b/src/assembler.cc index febae63..fa0b4c3 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -1061,6 +1061,12 @@ ExternalReference ExternalReference::delete_handle_scope_extensions( } +ExternalReference ExternalReference::random_uint32_function( + Isolate* isolate) { + return ExternalReference(Redirect(isolate, FUNCTION_ADDR(V8::Random))); +} + + ExternalReference ExternalReference::get_date_field_function( Isolate* isolate) { return ExternalReference(Redirect(isolate, FUNCTION_ADDR(JSDate::GetField))); diff --git a/src/assembler.h b/src/assembler.h index 0c706c4..049fa8c 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -718,6 +718,7 @@ class ExternalReference BASE_EMBEDDED { Isolate* isolate); static ExternalReference flush_icache_function(Isolate* isolate); static ExternalReference perform_gc_function(Isolate* isolate); + static ExternalReference random_uint32_function(Isolate* isolate); static ExternalReference transcendental_cache_array_address(Isolate* isolate); static ExternalReference delete_handle_scope_extensions(Isolate* isolate); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index dc64f17..bc52fa8 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -40,7 +40,6 @@ #include "objects-visiting.h" #include "platform.h" #include "snapshot.h" -#include "trig-table.h" #include "extensions/externalize-string-extension.h" #include "extensions/gc-extension.h" #include "extensions/statistics-extension.h" @@ -1309,6 +1308,10 @@ void Genesis::InitializeGlobal(Handle inner_global, // Initialize the embedder data slot. Handle embedder_data = factory->NewFixedArray(2); native_context()->set_embedder_data(*embedder_data); + + // Allocate the random seed slot. + Handle random_seed = factory->NewByteArray(kRandomStateSize); + native_context()->set_random_seed(*random_seed); } @@ -2632,71 +2635,13 @@ Genesis::Genesis(Isolate* isolate, InitializeExperimentalGlobal(); if (!InstallExperimentalNatives()) return; - // We can't (de-)serialize typed arrays currently, but we are lucky: The state - // of the random number generator and the trigonometric lookup tables needs no - // initialization during snapshot creation time. - uint32_t* state = NULL; - if (!Serializer::enabled()) { - // Initially seed the per-context random number generator using the - // per-isolate random number generator. - const int num_elems = 2; - state = new uint32_t[num_elems]; - const int num_bytes = num_elems * sizeof(*state); - - do { - isolate->random_number_generator()->NextBytes(state, num_bytes); - } while (state[0] == 0 || state[1] == 0); - - v8::Local buffer = v8::ArrayBuffer::New(state, num_bytes); - v8::Local ta = v8::Uint32Array::New(buffer, 0, num_elems); - Handle builtins(native_context()->builtins()); - ForceSetProperty(builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("rngstate")), - Utils::OpenHandle(*ta), - NONE); - - // Initialize trigonometric lookup tables and constants. - const int table_num_bytes = TrigonometricLookupTable::table_num_bytes(); - v8::Local sin_buffer = v8::ArrayBuffer::New( - TrigonometricLookupTable::sin_table(), table_num_bytes); - v8::Local cos_buffer = v8::ArrayBuffer::New( - TrigonometricLookupTable::cos_x_interval_table(), table_num_bytes); - v8::Local sin_table = v8::Float64Array::New( - sin_buffer, 0, TrigonometricLookupTable::table_size()); - v8::Local cos_table = v8::Float64Array::New( - cos_buffer, 0, TrigonometricLookupTable::table_size()); - - ForceSetProperty(builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kSinTable")), - Utils::OpenHandle(*sin_table), - NONE); - ForceSetProperty(builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kCosXIntervalTable")), - Utils::OpenHandle(*cos_table), - NONE); - ForceSetProperty(builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kSamples")), - factory()->NewHeapNumber( - TrigonometricLookupTable::samples()), - NONE); - ForceSetProperty(builtins, - factory()->InternalizeOneByteString( - STATIC_ASCII_VECTOR("kIndexConvert")), - factory()->NewHeapNumber( - TrigonometricLookupTable::samples_over_pi_half()), - NONE); - } - // TODO(svenpanne) We have to delete the state when the context dies, so we - // remember it in the context (encoded as a Smi, our usual technique for - // aligned pointers) and do the cleanup in - // WeakListVisitor::VisitPhantomObject(). This hack can go away when - // we have a way to allocate the backing store of typed arrays on the heap. - ASSERT(reinterpret_cast(state)->IsSmi()); - native_context()->set_random_state(reinterpret_cast(state)); + // Initially seed the per-context random number generator + // using the per-isolate random number generator. + uint32_t* state = reinterpret_cast( + native_context()->random_seed()->GetDataStartAddress()); + do { + isolate->random_number_generator()->NextBytes(state, kRandomStateSize); + } while (state[0] == 0 || state[1] == 0); result_ = native_context(); } diff --git a/src/contexts.h b/src/contexts.h index 3817de1..189c215 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -185,7 +185,7 @@ enum BindingFlags { V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \ generator_object_prototype_map) \ V(GENERATOR_RESULT_MAP_INDEX, Map, generator_result_map) \ - V(RANDOM_STATE_INDEX, Smi, random_state) + V(RANDOM_SEED_INDEX, ByteArray, random_seed) // JSFunctions are pairs (context, function code), sometimes also called // closures. A Context object is used to represent function contexts and @@ -332,7 +332,7 @@ class Context: public FixedArray { STRICT_MODE_GENERATOR_FUNCTION_MAP_INDEX, GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, GENERATOR_RESULT_MAP_INDEX, - RANDOM_STATE_INDEX, + RANDOM_SEED_INDEX, // Properties from here are treated as weak references by the full GC. // Scavenge treats them as strong references. diff --git a/src/globals.h b/src/globals.h index db80a7e..1db48a1 100644 --- a/src/globals.h +++ b/src/globals.h @@ -251,6 +251,9 @@ const int kFPOnStackSize = kRegisterSize; const int kDoubleSizeLog2 = 3; +// Size of the state of a the random number generator. +const int kRandomStateSize = 2 * kIntSize; + #if V8_HOST_ARCH_64_BIT const int kPointerSizeLog2 = 3; const intptr_t kIntptrSignBit = V8_INT64_C(0x8000000000000000); diff --git a/src/heap.cc b/src/heap.cc index dcab156..b5fe184 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1765,9 +1765,7 @@ struct WeakListVisitor { } } - static void VisitPhantomObject(Heap*, Context* context) { - // A bit of a hack, see the comment at the end of Genesis::Genesis(). - delete[] reinterpret_cast(context->random_state()); + static void VisitPhantomObject(Heap*, Context*) { } static int WeakNextOffset() { diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index f9524bc..73c1d7d 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -155,6 +155,7 @@ class LChunkBuilder; V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(Ror) \ @@ -4723,6 +4724,28 @@ class HPower V8_FINAL : public HTemplateInstruction<2> { }; +class HRandom V8_FINAL : public HTemplateInstruction<1> { + public: + DECLARE_INSTRUCTION_FACTORY_P1(HRandom, HValue*); + + HValue* global_object() { return OperandAt(0); } + + virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE { + return Representation::Tagged(); + } + + DECLARE_CONCRETE_INSTRUCTION(Random) + + private: + explicit HRandom(HValue* global_object) { + SetOperandAt(0, global_object); + set_representation(Representation::Double()); + } + + virtual bool IsDeletable() const V8_OVERRIDE { return true; } +}; + + class HAdd V8_FINAL : public HArithmeticBinaryOperation { public: static HInstruction* New(Zone* zone, diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 9215d0f..f7b3100 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -7489,6 +7489,16 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( return true; } break; + case kMathRandom: + if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) { + AddCheckConstantFunction(expr->holder(), receiver, receiver_map); + Drop(1); // Receiver. + HGlobalObject* global_object = Add(); + HRandom* result = New(global_object); + ast_context()->ReturnInstruction(result, expr->id()); + return true; + } + break; case kMathMax: case kMathMin: if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) { @@ -9863,6 +9873,14 @@ void HOptimizedGraphBuilder::GenerateLog(CallRuntime* call) { } +// Fast support for Math.random(). +void HOptimizedGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) { + HGlobalObject* global_object = Add(); + HRandom* result = New(global_object); + return ast_context()->ReturnInstruction(result, call->id()); +} + + // Fast support for StringAdd. void HOptimizedGraphBuilder::GenerateStringAdd(CallRuntime* call) { ASSERT_EQ(2, call->arguments()->length()); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index e067c90..86c525d 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -3296,6 +3296,57 @@ void FullCodeGenerator::EmitLog(CallRuntime* expr) { } +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); + + Label slow_allocate_heapnumber; + Label heapnumber_allocated; + + __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber); + __ jmp(&heapnumber_allocated); + + __ bind(&slow_allocate_heapnumber); + // Allocate a heap number. + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(edi, eax); + + __ bind(&heapnumber_allocated); + + __ PrepareCallCFunction(1, ebx); + __ mov(eax, ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX)); + __ mov(eax, FieldOperand(eax, GlobalObject::kNativeContextOffset)); + __ mov(Operand(esp, 0), eax); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); + + // Convert 32 random bits in eax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + // This is implemented on both SSE2 and FPU. + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatureScope fscope(masm(), SSE2); + __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(xmm1, ebx); + __ movd(xmm0, eax); + __ cvtss2sd(xmm1, xmm1); + __ xorps(xmm0, xmm1); + __ subsd(xmm0, xmm1); + __ movsd(FieldOperand(edi, HeapNumber::kValueOffset), xmm0); + } else { + // 0x4130000000000000 is 1.0 x 2^20 as a double. + __ mov(FieldOperand(edi, HeapNumber::kExponentOffset), + Immediate(0x41300000)); + __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax); + __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset)); + __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0)); + __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset)); + __ fsubp(1); + __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset)); + } + __ mov(eax, edi); + context()->Plug(eax); +} + + void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 1797d2e..f316e85 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -4167,6 +4167,69 @@ void LCodeGen::DoPower(LPower* instr) { } +void LCodeGen::DoRandom(LRandom* instr) { + CpuFeatureScope scope(masm(), SSE2); + + // Assert that the register size is indeed the size of each seed. + static const int kSeedSize = sizeof(uint32_t); + STATIC_ASSERT(kPointerSize == kSeedSize); + + // Load native context + Register global_object = ToRegister(instr->global_object()); + Register native_context = global_object; + __ mov(native_context, FieldOperand( + global_object, GlobalObject::kNativeContextOffset)); + + // Load state (FixedArray of the native context's random seeds) + static const int kRandomSeedOffset = + FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize; + Register state = native_context; + __ mov(state, FieldOperand(native_context, kRandomSeedOffset)); + + // Load state[0]. + Register state0 = ToRegister(instr->scratch()); + __ mov(state0, FieldOperand(state, ByteArray::kHeaderSize)); + // Load state[1]. + Register state1 = ToRegister(instr->scratch2()); + __ mov(state1, FieldOperand(state, ByteArray::kHeaderSize + kSeedSize)); + + // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16) + Register scratch3 = ToRegister(instr->scratch3()); + __ movzx_w(scratch3, state0); + __ imul(scratch3, scratch3, 18273); + __ shr(state0, 16); + __ add(state0, scratch3); + // Save state[0]. + __ mov(FieldOperand(state, ByteArray::kHeaderSize), state0); + + // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16) + __ movzx_w(scratch3, state1); + __ imul(scratch3, scratch3, 36969); + __ shr(state1, 16); + __ add(state1, scratch3); + // Save state[1]. + __ mov(FieldOperand(state, ByteArray::kHeaderSize + kSeedSize), state1); + + // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF) + Register random = state0; + __ shl(random, 14); + __ and_(state1, Immediate(0x3FFFF)); + __ add(random, state1); + + // Convert 32 random bits in random to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + XMMRegister result = ToDoubleRegister(instr->result()); + XMMRegister scratch4 = double_scratch0(); + __ mov(scratch3, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(scratch4, scratch3); + __ movd(result, random); + __ cvtss2sd(scratch4, scratch4); + __ xorps(result, scratch4); + __ subsd(result, scratch4); +} + + void LCodeGen::DoMathLog(LMathLog* instr) { CpuFeatureScope scope(masm(), SSE2); ASSERT(instr->value()->Equals(instr->result())); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 5343773..d4c17ab 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1695,6 +1695,19 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseTempRegister(instr->global_object()); + LOperand* scratch = TempRegister(); + LOperand* scratch2 = TempRegister(); + LOperand* scratch3 = TempRegister(); + LRandom* result = new(zone()) LRandom( + global_object, scratch, scratch2, scratch3); + return DefineFixedDouble(result, xmm1); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { ASSERT(instr->left()->representation().IsSmiOrTagged()); ASSERT(instr->right()->representation().IsSmiOrTagged()); diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index b429120..bc2b71b 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -154,6 +154,7 @@ class LCodeGen; V(OuterContext) \ V(Parameter) \ V(Power) \ + V(Random) \ V(PushArgument) \ V(RegExpLiteral) \ V(Return) \ @@ -1461,6 +1462,28 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> { }; +class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> { + public: + LRandom(LOperand* global_object, + LOperand* scratch, + LOperand* scratch2, + LOperand* scratch3) { + inputs_[0] = global_object; + temps_[0] = scratch; + temps_[1] = scratch2; + temps_[2] = scratch3; + } + + LOperand* global_object() const { return inputs_[0]; } + LOperand* scratch() const { return temps_[0]; } + LOperand* scratch2() const { return temps_[1]; } + LOperand* scratch3() const { return temps_[2]; } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD V8_FINAL : public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) diff --git a/src/math.js b/src/math.js index fb7d306..2df0ec2 100644 --- a/src/math.js +++ b/src/math.js @@ -79,8 +79,7 @@ function MathCeil(x) { // ECMA 262 - 15.8.2.7 function MathCos(x) { - x = MathAbs(x); // Convert to number and get rid of -0. - return TrigonometricInterpolation(x, 1); + return MathCosImpl(x); } // ECMA 262 - 15.8.2.8 @@ -169,15 +168,8 @@ function MathPow(x, y) { } // ECMA 262 - 15.8.2.14 -var rngstate; // Initialized to a Uint32Array during genesis. function MathRandom() { - var r0 = (MathImul(18273, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0; - rngstate[0] = r0; - var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0; - rngstate[1] = r1; - var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0; - // Division by 0x100000000 through multiplication by reciprocal. - return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10; + return %_RandomHeapNumber(); } // ECMA 262 - 15.8.2.15 @@ -187,9 +179,7 @@ function MathRound(x) { // ECMA 262 - 15.8.2.16 function MathSin(x) { - x = x * 1; // Convert to number and deal with -0. - if (%_IsMinusZero(x)) return x; - return TrigonometricInterpolation(x, 0); + return MathSinImpl(x); } // ECMA 262 - 15.8.2.17 @@ -199,7 +189,7 @@ function MathSqrt(x) { // ECMA 262 - 15.8.2.18 function MathTan(x) { - return MathSin(x) / MathCos(x); + return MathSinImpl(x) / MathCosImpl(x); } // Non-standard extension. @@ -208,73 +198,119 @@ function MathImul(x, y) { } -var kInversePiHalf = 0.636619772367581343; // 2 / pi -var kInversePiHalfS26 = 9.48637384723993156e-9; // 2 / pi / (2^26) -var kS26 = 1 << 26; -var kTwoStepThreshold = 1 << 27; -// pi / 2 rounded up -var kPiHalf = 1.570796326794896780; // 0x192d4454fb21f93f -// We use two parts for pi/2 to emulate a higher precision. -// pi_half_1 only has 26 significant bits for mantissa. -// Note that pi_half > pi_half_1 + pi_half_2 -var kPiHalf1 = 1.570796325802803040; // 0x00000054fb21f93f -var kPiHalf2 = 9.920935796805404252e-10; // 0x3326a611460b113e - -var kSamples; // Initialized to a number during genesis. -var kIndexConvert; // Initialized to kSamples / (pi/2) during genesis. -var kSinTable; // Initialized to a Float64Array during genesis. -var kCosXIntervalTable; // Initialized to a Float64Array during genesis. - -// This implements sine using the following algorithm. -// 1) Multiplication takes care of to-number conversion. -// 2) Reduce x to the first quadrant [0, pi/2]. -// Conveniently enough, in case of +/-Infinity, we get NaN. -// Note that we try to use only 26 instead of 52 significant bits for -// mantissa to avoid rounding errors when multiplying. For very large -// input we therefore have additional steps. -// 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant. -// 4) Do a table lookup for the closest samples to the left and right of x. -// 5) Find the derivatives at those sampling points by table lookup: -// dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2]. -// 6) Use cubic spline interpolation to approximate sin(x). -// 7) Negate the result if x was in the 3rd or 4th quadrant. -// 8) Get rid of -0 by adding 0. -function TrigonometricInterpolation(x, phase) { - if (x < 0 || x > kPiHalf) { - var multiple; - while (x < -kTwoStepThreshold || x > kTwoStepThreshold) { - // Let's assume this loop does not terminate. - // All numbers x in each loop forms a set S. - // (1) abs(x) > 2^27 for all x in S. - // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1 - // (3) multiple is rounded down in 2^26 steps, so the rounding error is - // at most max(ulp, 2^26). - // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least - // (1-pi/4)x - // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4. - // Note that this difference cannot be simply rounded off. - // Set S cannot exist since (5) violates (1). Loop must terminate. - multiple = MathFloor(x * kInversePiHalfS26) * kS26; - x = x - multiple * kPiHalf1 - multiple * kPiHalf2; +var MathSinImpl = function(x) { + InitTrigonometricFunctions(); + return MathSinImpl(x); +} + + +var MathCosImpl = function(x) { + InitTrigonometricFunctions(); + return MathCosImpl(x); +} + + +var InitTrigonometricFunctions; + + +// Define constants and interpolation functions. +// Also define the initialization function that populates the lookup table +// and then wires up the function definitions. +function SetupTrigonometricFunctions() { + var samples = 1800; // Table size. Do not change arbitrarily. + var inverse_pi_half = 0.636619772367581343; // 2 / pi + var inverse_pi_half_s_26 = 9.48637384723993156e-9; // 2 / pi / (2^26) + var s_26 = 1 << 26; + var two_step_threshold = 1 << 27; + var index_convert = 1145.915590261646418; // samples / (pi / 2) + // pi / 2 rounded up + var pi_half = 1.570796326794896780; // 0x192d4454fb21f93f + // We use two parts for pi/2 to emulate a higher precision. + // pi_half_1 only has 26 significant bits for mantissa. + // Note that pi_half > pi_half_1 + pi_half_2 + var pi_half_1 = 1.570796325802803040; // 0x00000054fb21f93f + var pi_half_2 = 9.920935796805404252e-10; // 0x3326a611460b113e + var table_sin; + var table_cos_interval; + + // This implements sine using the following algorithm. + // 1) Multiplication takes care of to-number conversion. + // 2) Reduce x to the first quadrant [0, pi/2]. + // Conveniently enough, in case of +/-Infinity, we get NaN. + // Note that we try to use only 26 instead of 52 significant bits for + // mantissa to avoid rounding errors when multiplying. For very large + // input we therefore have additional steps. + // 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant. + // 4) Do a table lookup for the closest samples to the left and right of x. + // 5) Find the derivatives at those sampling points by table lookup: + // dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2]. + // 6) Use cubic spline interpolation to approximate sin(x). + // 7) Negate the result if x was in the 3rd or 4th quadrant. + // 8) Get rid of -0 by adding 0. + var Interpolation = function(x, phase) { + if (x < 0 || x > pi_half) { + var multiple; + while (x < -two_step_threshold || x > two_step_threshold) { + // Let's assume this loop does not terminate. + // All numbers x in each loop forms a set S. + // (1) abs(x) > 2^27 for all x in S. + // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1 + // (3) multiple is rounded down in 2^26 steps, so the rounding error is + // at most max(ulp, 2^26). + // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least + // (1-pi/4)x + // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4. + // Note that this difference cannot be simply rounded off. + // Set S cannot exist since (5) violates (1). Loop must terminate. + multiple = MathFloor(x * inverse_pi_half_s_26) * s_26; + x = x - multiple * pi_half_1 - multiple * pi_half_2; + } + multiple = MathFloor(x * inverse_pi_half); + x = x - multiple * pi_half_1 - multiple * pi_half_2; + phase += multiple; } - multiple = MathFloor(x * kInversePiHalf); - x = x - multiple * kPiHalf1 - multiple * kPiHalf2; - phase += multiple; + var double_index = x * index_convert; + if (phase & 1) double_index = samples - double_index; + var index = double_index | 0; + var t1 = double_index - index; + var t2 = 1 - t1; + var y1 = table_sin[index]; + var y2 = table_sin[index + 1]; + var dy = y2 - y1; + return (t2 * y1 + t1 * y2 + + t1 * t2 * ((table_cos_interval[index] - dy) * t2 + + (dy - table_cos_interval[index + 1]) * t1)) + * (1 - (phase & 2)) + 0; + } + + var MathSinInterpolation = function(x) { + x = x * 1; // Convert to number and deal with -0. + if (%_IsMinusZero(x)) return x; + return Interpolation(x, 0); + } + + // Cosine is sine with a phase offset. + var MathCosInterpolation = function(x) { + x = MathAbs(x); // Convert to number and get rid of -0. + return Interpolation(x, 1); + }; + + %SetInlineBuiltinFlag(Interpolation); + %SetInlineBuiltinFlag(MathSinInterpolation); + %SetInlineBuiltinFlag(MathCosInterpolation); + + InitTrigonometricFunctions = function() { + table_sin = new global.Float64Array(samples + 2); + table_cos_interval = new global.Float64Array(samples + 2); + %PopulateTrigonometricTable(table_sin, table_cos_interval, samples); + MathSinImpl = MathSinInterpolation; + MathCosImpl = MathCosInterpolation; } - var double_index = x * kIndexConvert; - if (phase & 1) double_index = kSamples - double_index; - var index = double_index | 0; - var t1 = double_index - index; - var t2 = 1 - t1; - var y1 = kSinTable[index]; - var y2 = kSinTable[index + 1]; - var dy = y2 - y1; - return (t2 * y1 + t1 * y2 + - t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 + - (dy - kCosXIntervalTable[index + 1]) * t1)) - * (1 - (phase & 2)) + 0; } +SetupTrigonometricFunctions(); + + // ------------------------------------------------------------------- function SetUpMath() { @@ -351,7 +387,6 @@ function SetUpMath() { %SetInlineBuiltinFlag(MathSin); %SetInlineBuiltinFlag(MathCos); %SetInlineBuiltinFlag(MathTan); - %SetInlineBuiltinFlag(TrigonometricInterpolation); } SetUpMath(); diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index 486eb9d..6ef871f 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -3378,6 +3378,48 @@ void FullCodeGenerator::EmitLog(CallRuntime* expr) { } +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); + Label slow_allocate_heapnumber; + Label heapnumber_allocated; + + // Save the new heap number in callee-saved register s0, since + // we call out to external C code below. + __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(s0, a1, a2, t6, &slow_allocate_heapnumber); + __ jmp(&heapnumber_allocated); + + __ bind(&slow_allocate_heapnumber); + + // Allocate a heap number. + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ mov(s0, v0); // Save result in s0, so it is saved thru CFunc call. + + __ bind(&heapnumber_allocated); + + // Convert 32 random bits in v0 to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + __ PrepareCallCFunction(1, a0); + __ lw(a0, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX)); + __ lw(a0, FieldMemOperand(a0, GlobalObject::kNativeContextOffset)); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); + + // 0x41300000 is the top half of 1.0 x 2^20 as a double. + __ li(a1, Operand(0x41300000)); + // Move 0x41300000xxxxxxxx (x = random bits in v0) to FPU. + __ Move(f12, v0, a1); + // Move 0x4130000000000000 to FPU. + __ Move(f14, zero_reg, a1); + // Subtract and store the result in the heap number. + __ sub_d(f0, f12, f14); + __ sdc1(f0, FieldMemOperand(s0, HeapNumber::kValueOffset)); + __ mov(v0, s0); + + context()->Plug(v0); +} + + void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index 5a8249e..43d83bd 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -3851,6 +3851,68 @@ void LCodeGen::DoPower(LPower* instr) { } +void LCodeGen::DoRandom(LRandom* instr) { + // Assert that the register size is indeed the size of each seed. + static const int kSeedSize = sizeof(uint32_t); + STATIC_ASSERT(kPointerSize == kSeedSize); + + // Load native context. + Register global_object = ToRegister(instr->global_object()); + Register native_context = global_object; + __ lw(native_context, FieldMemOperand( + global_object, GlobalObject::kNativeContextOffset)); + + // Load state (FixedArray of the native context's random seeds). + static const int kRandomSeedOffset = + FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize; + Register state = native_context; + __ lw(state, FieldMemOperand(native_context, kRandomSeedOffset)); + + // Load state[0]. + Register state0 = ToRegister(instr->scratch()); + __ lw(state0, FieldMemOperand(state, ByteArray::kHeaderSize)); + // Load state[1]. + Register state1 = ToRegister(instr->scratch2()); + __ lw(state1, FieldMemOperand(state, ByteArray::kHeaderSize + kSeedSize)); + + // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16) + Register scratch3 = ToRegister(instr->scratch3()); + Register scratch4 = scratch0(); + __ And(scratch3, state0, Operand(0xFFFF)); + __ li(scratch4, Operand(18273)); + __ Mul(scratch3, scratch3, scratch4); + __ srl(state0, state0, 16); + __ Addu(state0, scratch3, state0); + // Save state[0]. + __ sw(state0, FieldMemOperand(state, ByteArray::kHeaderSize)); + + // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16) + __ And(scratch3, state1, Operand(0xFFFF)); + __ li(scratch4, Operand(36969)); + __ Mul(scratch3, scratch3, scratch4); + __ srl(state1, state1, 16), + __ Addu(state1, scratch3, state1); + // Save state[1]. + __ sw(state1, FieldMemOperand(state, ByteArray::kHeaderSize + kSeedSize)); + + // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF) + Register random = scratch4; + __ And(random, state1, Operand(0x3FFFF)); + __ sll(state0, state0, 14); + __ Addu(random, random, state0); + + // 0x41300000 is the top half of 1.0 x 2^20 as a double. + __ li(scratch3, Operand(0x41300000)); + // Move 0x41300000xxxxxxxx (x = random bits in v0) to FPU. + DoubleRegister result = ToDoubleRegister(instr->result()); + __ Move(result, random, scratch3); + // Move 0x4130000000000000 to FPU. + DoubleRegister scratch5 = double_scratch0(); + __ Move(scratch5, zero_reg, scratch3); + __ sub_d(result, result, scratch5); +} + + void LCodeGen::DoMathExp(LMathExp* instr) { DoubleRegister input = ToDoubleRegister(instr->value()); DoubleRegister result = ToDoubleRegister(instr->result()); diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index 34e5838..5dbef8d 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -1640,6 +1640,19 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseTempRegister(instr->global_object()); + LOperand* scratch = TempRegister(); + LOperand* scratch2 = TempRegister(); + LOperand* scratch3 = TempRegister(); + LRandom* result = new(zone()) LRandom( + global_object, scratch, scratch2, scratch3); + return DefineFixedDouble(result, f0); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { ASSERT(instr->left()->representation().IsTagged()); ASSERT(instr->right()->representation().IsTagged()); diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h index 2996c78..452963b 100644 --- a/src/mips/lithium-mips.h +++ b/src/mips/lithium-mips.h @@ -154,6 +154,7 @@ class LCodeGen; V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(SeqStringGetChar) \ @@ -1460,6 +1461,28 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> { }; +class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> { + public: + LRandom(LOperand* global_object, + LOperand* scratch, + LOperand* scratch2, + LOperand* scratch3) { + inputs_[0] = global_object; + temps_[0] = scratch; + temps_[1] = scratch2; + temps_[2] = scratch3; + } + + LOperand* global_object() const { return inputs_[0]; } + LOperand* scratch() const { return temps_[0]; } + LOperand* scratch2() const { return temps_[1]; } + LOperand* scratch3() const { return temps_[2]; } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD V8_FINAL : public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) diff --git a/src/objects.h b/src/objects.h index 1e26178..a5f5b52 100644 --- a/src/objects.h +++ b/src/objects.h @@ -6507,6 +6507,7 @@ class Script: public Struct { V(Math, exp, MathExp) \ V(Math, sqrt, MathSqrt) \ V(Math, pow, MathPow) \ + V(Math, random, MathRandom) \ V(Math, max, MathMax) \ V(Math, min, MathMin) \ V(Math, imul, MathImul) diff --git a/src/runtime.cc b/src/runtime.cc index c11a005..f546629 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -7848,6 +7848,35 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_tan) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_PopulateTrigonometricTable) { + HandleScope scope(isolate); + ASSERT(args.length() == 3); + CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sin_table, 0); + CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, cos_table, 1); + CONVERT_SMI_ARG_CHECKED(samples, 2); + RUNTIME_ASSERT(sin_table->type() == kExternalDoubleArray); + RUNTIME_ASSERT(cos_table->type() == kExternalDoubleArray); + double* sin_buffer = reinterpret_cast( + JSArrayBuffer::cast(sin_table->buffer())->backing_store()); + double* cos_buffer = reinterpret_cast( + JSArrayBuffer::cast(cos_table->buffer())->backing_store()); + + static const double pi_half = 3.1415926535897932 / 2; + double interval = pi_half / samples; + for (int i = 0; i < samples + 1; i++) { + double sample = sin(i * interval); + sin_buffer[i] = sample; + cos_buffer[samples - i] = sample * interval; + } + + // Fill this to catch out of bound accesses when calculating Math.sin(pi/2). + sin_buffer[samples + 1] = sin(pi_half + interval); + cos_buffer[samples + 1] = cos(pi_half + interval) * interval; + + return isolate->heap()->undefined_value(); +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) { SealHandleScope shs(isolate); ASSERT(args.length() == 2); diff --git a/src/runtime.h b/src/runtime.h index 43d4065..4872ab5 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -190,6 +190,7 @@ namespace internal { F(Math_sin, 1, 1) \ F(Math_sqrt, 1, 1) \ F(Math_tan, 1, 1) \ + F(PopulateTrigonometricTable, 3, 1) \ \ /* Regular expressions */ \ F(RegExpCompile, 3, 1) \ @@ -622,6 +623,7 @@ namespace internal { F(OneByteSeqStringSetChar, 3, 1) \ F(TwoByteSeqStringSetChar, 3, 1) \ F(ObjectEquals, 2, 1) \ + F(RandomHeapNumber, 0, 1) \ F(IsObject, 1, 1) \ F(IsFunction, 1, 1) \ F(IsUndetectableObject, 1, 1) \ diff --git a/src/serialize.cc b/src/serialize.cc index a8df238..36e19c1 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -297,6 +297,10 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { RUNTIME_ENTRY, 1, "Runtime::PerformGC"); + Add(ExternalReference::random_uint32_function(isolate).address(), + RUNTIME_ENTRY, + 3, + "V8::Random"); Add(ExternalReference::delete_handle_scope_extensions(isolate).address(), RUNTIME_ENTRY, 4, diff --git a/src/trig-table.h b/src/trig-table.h deleted file mode 100644 index 081c038..0000000 --- a/src/trig-table.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef V8_TRIG_TABLE_H_ -#define V8_TRIG_TABLE_H_ - - -namespace v8 { -namespace internal { - -class TrigonometricLookupTable : public AllStatic { - public: - // Casting away const-ness to use as argument for typed array constructor. - static void* sin_table() { - return const_cast(&kSinTable[0]); - } - - static void* cos_x_interval_table() { - return const_cast(&kCosXIntervalTable[0]); - } - - static double samples_over_pi_half() { return kSamplesOverPiHalf; } - static int samples() { return kSamples; } - static int table_num_bytes() { return kTableSize * sizeof(*kSinTable); } - static int table_size() { return kTableSize; } - - private: - static const double kSinTable[]; - static const double kCosXIntervalTable[]; - static const int kSamples; - static const int kTableSize; - static const double kSamplesOverPiHalf; -}; - -} } // namespace v8::internal - -#endif // V8_TRIG_TABLE_H_ diff --git a/src/v8.cc b/src/v8.cc index 0f01818..c56c82a 100644 --- a/src/v8.cc +++ b/src/v8.cc @@ -119,6 +119,25 @@ void V8::SetReturnAddressLocationResolver( } +// Used by JavaScript APIs +uint32_t V8::Random(Context* context) { + ASSERT(context->IsNativeContext()); + ByteArray* seed = context->random_seed(); + uint32_t* state = reinterpret_cast(seed->GetDataStartAddress()); + + // When we get here, the RNG must have been initialized, + // see the Genesis constructor in file bootstrapper.cc. + ASSERT_NE(0, state[0]); + ASSERT_NE(0, state[1]); + + // Mix the bits. Never replaces state[i] with 0 if it is nonzero. + state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16); + state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16); + + return (state[0] << 14) + (state[1] & 0x3FFFF); +} + + void V8::AddCallCompletedCallback(CallCompletedCallback callback) { if (call_completed_callbacks_ == NULL) { // Lazy init. call_completed_callbacks_ = new List(); diff --git a/src/v8.h b/src/v8.h index 8069e8a..6f2633a 100644 --- a/src/v8.h +++ b/src/v8.h @@ -96,6 +96,8 @@ class V8 : public AllStatic { ReturnAddressLocationResolver resolver); // Support for entry hooking JITed code. static void SetFunctionEntryHook(FunctionEntryHook entry_hook); + // Random number generation support. Not cryptographically safe. + static uint32_t Random(Context* context); static void AddCallCompletedCallback(CallCompletedCallback callback); static void RemoveCallCompletedCallback(CallCompletedCallback callback); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index a62fde0..71b5468 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -3271,6 +3271,47 @@ void FullCodeGenerator::EmitLog(CallRuntime* expr) { } +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); + + Label slow_allocate_heapnumber; + Label heapnumber_allocated; + + __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber); + __ jmp(&heapnumber_allocated); + + __ bind(&slow_allocate_heapnumber); + // Allocate a heap number. + __ CallRuntime(Runtime::kNumberAlloc, 0); + __ movq(rbx, rax); + + __ bind(&heapnumber_allocated); + + // Return a random uint32 number in rax. + // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs. + __ PrepareCallCFunction(1); + __ movq(arg_reg_1, + ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX)); + __ movq(arg_reg_1, + FieldOperand(arg_reg_1, GlobalObject::kNativeContextOffset)); + __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); + + // Convert 32 random bits in rax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single. + __ movd(xmm1, rcx); + __ movd(xmm0, rax); + __ cvtss2sd(xmm1, xmm1); + __ xorps(xmm0, xmm1); + __ subsd(xmm0, xmm1); + __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0); + + __ movq(rax, rbx); + context()->Plug(rax); +} + + void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 241963b..0c95745 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -3698,6 +3698,66 @@ void LCodeGen::DoPower(LPower* instr) { } +void LCodeGen::DoRandom(LRandom* instr) { + // Assert that register size is twice the size of each seed. + static const int kSeedSize = sizeof(uint32_t); + STATIC_ASSERT(kPointerSize == 2 * kSeedSize); + + // Load native context + Register global_object = ToRegister(instr->global_object()); + Register native_context = global_object; + __ movq(native_context, FieldOperand( + global_object, GlobalObject::kNativeContextOffset)); + + // Load state (FixedArray of the native context's random seeds) + static const int kRandomSeedOffset = + FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize; + Register state = native_context; + __ movq(state, FieldOperand(native_context, kRandomSeedOffset)); + + // Load state[0]. + Register state0 = ToRegister(instr->scratch()); + __ movl(state0, FieldOperand(state, ByteArray::kHeaderSize)); + // Load state[1]. + Register state1 = ToRegister(instr->scratch2()); + __ movl(state1, FieldOperand(state, ByteArray::kHeaderSize + kSeedSize)); + + // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16) + Register scratch3 = ToRegister(instr->scratch3()); + __ movzxwl(scratch3, state0); + __ imull(scratch3, scratch3, Immediate(18273)); + __ shrl(state0, Immediate(16)); + __ addl(state0, scratch3); + // Save state[0]. + __ movl(FieldOperand(state, ByteArray::kHeaderSize), state0); + + // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16) + __ movzxwl(scratch3, state1); + __ imull(scratch3, scratch3, Immediate(36969)); + __ shrl(state1, Immediate(16)); + __ addl(state1, scratch3); + // Save state[1]. + __ movl(FieldOperand(state, ByteArray::kHeaderSize + kSeedSize), state1); + + // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF) + Register random = state0; + __ shll(random, Immediate(14)); + __ andl(state1, Immediate(0x3FFFF)); + __ addl(random, state1); + + // Convert 32 random bits in rax to 0.(32 random bits) in a double + // by computing: + // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). + XMMRegister result = ToDoubleRegister(instr->result()); + XMMRegister scratch4 = double_scratch0(); + __ movq(scratch3, V8_INT64_C(0x4130000000000000)); // 1.0 x 2^20 as double + __ movq(scratch4, scratch3); + __ movd(result, random); + __ xorps(result, scratch4); + __ subsd(result, scratch4); +} + + void LCodeGen::DoMathExp(LMathExp* instr) { XMMRegister input = ToDoubleRegister(instr->value()); XMMRegister result = ToDoubleRegister(instr->result()); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index fa1bdaa..2a73472 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1601,6 +1601,19 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) { } +LInstruction* LChunkBuilder::DoRandom(HRandom* instr) { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->global_object()->representation().IsTagged()); + LOperand* global_object = UseTempRegister(instr->global_object()); + LOperand* scratch = TempRegister(); + LOperand* scratch2 = TempRegister(); + LOperand* scratch3 = TempRegister(); + LRandom* result = new(zone()) LRandom( + global_object, scratch, scratch2, scratch3); + return DefineFixedDouble(result, xmm1); +} + + LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { ASSERT(instr->left()->representation().IsTagged()); ASSERT(instr->right()->representation().IsTagged()); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index ca7831c..a475d8c 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -153,6 +153,7 @@ class LCodeGen; V(Parameter) \ V(Power) \ V(PushArgument) \ + V(Random) \ V(RegExpLiteral) \ V(Return) \ V(SeqStringGetChar) \ @@ -1423,6 +1424,28 @@ class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> { }; +class LRandom V8_FINAL : public LTemplateInstruction<1, 1, 3> { + public: + LRandom(LOperand* global_object, + LOperand* scratch, + LOperand* scratch2, + LOperand* scratch3) { + inputs_[0] = global_object; + temps_[0] = scratch; + temps_[1] = scratch2; + temps_[2] = scratch3; + } + + LOperand* global_object() { return inputs_[0]; } + LOperand* scratch() const { return temps_[0]; } + LOperand* scratch2() const { return temps_[1]; } + LOperand* scratch3() const { return temps_[2]; } + + DECLARE_CONCRETE_INSTRUCTION(Random, "random") + DECLARE_HYDROGEN_ACCESSOR(Random) +}; + + class LArithmeticD V8_FINAL : public LTemplateInstruction<1, 2, 0> { public: LArithmeticD(Token::Value op, LOperand* left, LOperand* right) diff --git a/test/cctest/test-weaktypedarrays.cc b/test/cctest/test-weaktypedarrays.cc index edfc375..fe1ef04 100644 --- a/test/cctest/test-weaktypedarrays.cc +++ b/test/cctest/test-weaktypedarrays.cc @@ -89,7 +89,7 @@ TEST(WeakArrayBuffersFromApi) { LocalContext context; Isolate* isolate = GetIsolateFrom(&context); - int start = CountArrayBuffersInWeakList(isolate->heap()); + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); { v8::HandleScope s1(context->GetIsolate()); v8::Handle ab1 = v8::ArrayBuffer::New(256); @@ -99,12 +99,12 @@ TEST(WeakArrayBuffersFromApi) { Handle iab1 = v8::Utils::OpenHandle(*ab1); Handle iab2 = v8::Utils::OpenHandle(*ab2); - CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap())); CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab1)); CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab2)); } isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); { HandleScope scope2(isolate); Handle iab1 = v8::Utils::OpenHandle(*ab1); @@ -114,7 +114,7 @@ TEST(WeakArrayBuffersFromApi) { } isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); } @@ -122,12 +122,11 @@ TEST(WeakArrayBuffersFromScript) { v8::V8::Initialize(); LocalContext context; Isolate* isolate = GetIsolateFrom(&context); - int start = CountArrayBuffersInWeakList(isolate->heap()); for (int i = 1; i <= 3; i++) { // Create 3 array buffers, make i-th of them garbage, // validate correct state of array buffer weak list. - CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); { v8::HandleScope scope(context->GetIsolate()); @@ -143,7 +142,7 @@ TEST(WeakArrayBuffersFromScript) { v8::Handle ab3 = v8::Handle::Cast(CompileRun("ab3")); - CHECK_EQ(3, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(3, CountArrayBuffersInWeakList(isolate->heap())); CHECK(HasArrayBufferInWeakList(isolate->heap(), *v8::Utils::OpenHandle(*ab1))); CHECK(HasArrayBufferInWeakList(isolate->heap(), @@ -157,7 +156,7 @@ TEST(WeakArrayBuffersFromScript) { CompileRun(source.start()); isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap())); { v8::HandleScope s2(context->GetIsolate()); @@ -175,7 +174,7 @@ TEST(WeakArrayBuffersFromScript) { } isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); } } @@ -267,7 +266,6 @@ static void TestTypedArrayFromScript(const char* constructor) { LocalContext context; Isolate* isolate = GetIsolateFrom(&context); v8::HandleScope scope(context->GetIsolate()); - int start = CountArrayBuffersInWeakList(isolate->heap()); CompileRun("var ab = new ArrayBuffer(2048);"); for (int i = 1; i <= 3; i++) { // Create 3 typed arrays, make i-th of them garbage, @@ -275,7 +273,7 @@ static void TestTypedArrayFromScript(const char* constructor) { v8::HandleScope s0(context->GetIsolate()); i::ScopedVector source(2048); - CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); { v8::HandleScope s1(context->GetIsolate()); @@ -294,7 +292,7 @@ static void TestTypedArrayFromScript(const char* constructor) { v8::Handle::Cast(CompileRun("ta2")); v8::Handle ta3 = v8::Handle::Cast(CompileRun("ta3")); - CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); Handle iab = v8::Utils::OpenHandle(*ab); CHECK_EQ(3, CountViews(*iab)); CHECK(HasViewInWeakList(*iab, *v8::Utils::OpenHandle(*ta1))); @@ -306,7 +304,7 @@ static void TestTypedArrayFromScript(const char* constructor) { CompileRun(source.start()); isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); { v8::HandleScope s2(context->GetIsolate()); @@ -326,7 +324,7 @@ static void TestTypedArrayFromScript(const char* constructor) { CompileRun("ta1 = null; ta2 = null; ta3 = null;"); isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); { v8::HandleScope s3(context->GetIsolate()); diff --git a/tools/generate-trig-table.py b/tools/generate-trig-table.py deleted file mode 100644 index 14d4472..0000000 --- a/tools/generate-trig-table.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2013 the V8 project authors. All rights reserved. -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# This is a utility for populating the lookup table for the -# approximation of trigonometric functions. - -import sys, math - -SAMPLES = 1800 - -TEMPLATE = """\ -// Copyright 2013 Google Inc. All Rights Reserved. - -// This file was generated from a python script. - -#include "v8.h" -#include "trig-table.h" - -namespace v8 { -namespace internal { - - const double TrigonometricLookupTable::kSinTable[] = - { %(sine_table)s }; - const double TrigonometricLookupTable::kCosXIntervalTable[] = - { %(cosine_table)s }; - const int TrigonometricLookupTable::kSamples = %(samples)i; - const int TrigonometricLookupTable::kTableSize = %(table_size)i; - const double TrigonometricLookupTable::kSamplesOverPiHalf = - %(samples_over_pi_half)s; - -} } // v8::internal -""" - -def main(): - pi_half = math.pi / 2 - interval = pi_half / SAMPLES - sin = [] - cos_times_interval = [] - table_size = SAMPLES + 2 - - for i in range(0, table_size): - sample = i * interval - sin.append(repr(math.sin(sample))) - cos_times_interval.append(repr(math.cos(sample) * interval)) - - output_file = sys.argv[1] - output = open(str(output_file), "w") - output.write(TEMPLATE % { - 'sine_table': ','.join(sin), - 'cosine_table': ','.join(cos_times_interval), - 'samples': SAMPLES, - 'table_size': table_size, - 'samples_over_pi_half': repr(SAMPLES / pi_half) - }) - -if __name__ == "__main__": - main() - diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 65d5fb8..b1b9019 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -140,7 +140,6 @@ 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc', '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc', - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', '<(INTERMEDIATE_DIR)/snapshot.cc', ], 'actions': [ @@ -183,7 +182,6 @@ 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc', '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc', - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', '../../src/snapshot-empty.cc', ], 'conditions': [ @@ -202,38 +200,9 @@ }], ] }, - { 'target_name': 'generate_trig_table', - 'type': 'none', - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host', 'target'], - }, { - 'toolsets': ['target'], - }], - ], - 'actions': [ - { - 'action_name': 'generate', - 'inputs': [ - '../../tools/generate-trig-table.py', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/trig-table.cc', - ], - 'action': [ - 'python', - '../../tools/generate-trig-table.py', - '<@(_outputs)', - ], - }, - ] - }, { 'target_name': 'v8_base.<(v8_target_arch)', 'type': 'static_library', - 'dependencies': [ - 'generate_trig_table', - ], 'variables': { 'optimize': 'max', },