Commit 0d8e5212 authored by yangguo's avatar yangguo Committed by Commit bot

[Math] implement Math.random as TFJ builtin.

R=bmeurer@chromium.org
BUG=v8:5049, v8:5086

Review-Url: https://codereview.chromium.org/2402363002
Cr-Commit-Position: refs/heads/master@{#40149}
parent 3d5ae0f7
...@@ -398,7 +398,6 @@ action("js2c") { ...@@ -398,7 +398,6 @@ action("js2c") {
"src/js/symbol.js", "src/js/symbol.js",
"src/js/array.js", "src/js/array.js",
"src/js/string.js", "src/js/string.js",
"src/js/math.js",
"src/js/regexp.js", "src/js/regexp.js",
"src/js/arraybuffer.js", "src/js/arraybuffer.js",
"src/js/typedarray.js", "src/js/typedarray.js",
......
...@@ -1951,6 +1951,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1951,6 +1951,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> math_pow = Handle<JSFunction> math_pow =
SimpleInstallFunction(math, "pow", Builtins::kMathPow, 2, true); SimpleInstallFunction(math, "pow", Builtins::kMathPow, 2, true);
native_context()->set_math_pow(*math_pow); native_context()->set_math_pow(*math_pow);
SimpleInstallFunction(math, "random", Builtins::kMathRandom, 0, true);
SimpleInstallFunction(math, "round", Builtins::kMathRound, 1, true); SimpleInstallFunction(math, "round", Builtins::kMathRound, 1, true);
SimpleInstallFunction(math, "sign", Builtins::kMathSign, 1, true); SimpleInstallFunction(math, "sign", Builtins::kMathSign, 1, true);
SimpleInstallFunction(math, "sin", Builtins::kMathSin, 1, true); SimpleInstallFunction(math, "sin", Builtins::kMathSin, 1, true);
...@@ -1993,6 +1994,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1993,6 +1994,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
math, factory->NewStringFromAsciiChecked("SQRT2"), math, factory->NewStringFromAsciiChecked("SQRT2"),
factory->NewNumber(std::sqrt(2.0)), factory->NewNumber(std::sqrt(2.0)),
static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY)); static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY));
JSObject::AddProperty(
math, factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("Math"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
} }
{ // -- A r r a y B u f f e r { // -- A r r a y B u f f e r
......
...@@ -452,6 +452,46 @@ void Builtins::Generate_MathPow(CodeStubAssembler* assembler) { ...@@ -452,6 +452,46 @@ void Builtins::Generate_MathPow(CodeStubAssembler* assembler) {
assembler->Return(result); assembler->Return(result);
} }
// ES6 section 20.2.2.27 Math.random ( )
void Builtins::Generate_MathRandom(CodeStubAssembler* assembler) {
using compiler::Node;
Node* context = assembler->Parameter(3);
Node* native_context = assembler->LoadNativeContext(context);
// Load cache index.
CodeStubAssembler::Variable smi_index(assembler,
MachineRepresentation::kTagged);
smi_index.Bind(assembler->LoadContextElement(
native_context, Context::MATH_RANDOM_INDEX_INDEX));
// Cached random numbers are exhausted if index is 0. Go to slow path.
CodeStubAssembler::Label if_cached(assembler);
assembler->GotoIf(assembler->SmiAbove(smi_index.value(),
assembler->SmiConstant(Smi::kZero)),
&if_cached);
// Cache exhausted, populate the cache. Return value is the new index.
smi_index.Bind(
assembler->CallRuntime(Runtime::kGenerateRandomNumbers, context));
assembler->Goto(&if_cached);
// Compute next index by decrement.
assembler->Bind(&if_cached);
Node* new_smi_index = assembler->SmiSub(
smi_index.value(), assembler->SmiConstant(Smi::FromInt(1)));
assembler->StoreContextElement(
native_context, Context::MATH_RANDOM_INDEX_INDEX, new_smi_index);
// Load and return next cached random number.
Node* array = assembler->LoadContextElement(native_context,
Context::MATH_RANDOM_CACHE_INDEX);
Node* random = assembler->LoadFixedDoubleArrayElement(
array, new_smi_index, MachineType::Float64(), 0,
CodeStubAssembler::SMI_PARAMETERS);
assembler->Return(assembler->ChangeFloat64ToTagged(random));
}
// ES6 section 20.2.2.28 Math.round ( x ) // ES6 section 20.2.2.28 Math.round ( x )
void Builtins::Generate_MathRound(CodeStubAssembler* assembler) { void Builtins::Generate_MathRound(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Round); Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Round);
......
...@@ -450,6 +450,8 @@ namespace internal { ...@@ -450,6 +450,8 @@ namespace internal {
ASM(MathMin) \ ASM(MathMin) \
/* ES6 section 20.2.2.26 Math.pow ( x, y ) */ \ /* ES6 section 20.2.2.26 Math.pow ( x, y ) */ \
TFJ(MathPow, 3) \ TFJ(MathPow, 3) \
/* ES6 section 20.2.2.27 Math.random */ \
TFJ(MathRandom, 1) \
/* ES6 section 20.2.2.28 Math.round ( x ) */ \ /* ES6 section 20.2.2.28 Math.round ( x ) */ \
TFJ(MathRound, 2) \ TFJ(MathRound, 2) \
/* ES6 section 20.2.2.29 Math.sign ( x ) */ \ /* ES6 section 20.2.2.29 Math.sign ( x ) */ \
......
...@@ -1092,6 +1092,13 @@ Node* CodeStubAssembler::LoadContextElement(Node* context, int slot_index) { ...@@ -1092,6 +1092,13 @@ Node* CodeStubAssembler::LoadContextElement(Node* context, int slot_index) {
return Load(MachineType::AnyTagged(), context, IntPtrConstant(offset)); return Load(MachineType::AnyTagged(), context, IntPtrConstant(offset));
} }
Node* CodeStubAssembler::StoreContextElement(Node* context, int slot_index,
Node* value) {
int offset = Context::SlotOffset(slot_index);
return Store(MachineRepresentation::kTagged, context, IntPtrConstant(offset),
value);
}
Node* CodeStubAssembler::LoadNativeContext(Node* context) { Node* CodeStubAssembler::LoadNativeContext(Node* context) {
return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX); return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX);
} }
......
...@@ -298,6 +298,8 @@ class CodeStubAssembler : public compiler::CodeAssembler { ...@@ -298,6 +298,8 @@ class CodeStubAssembler : public compiler::CodeAssembler {
// Context manipulation // Context manipulation
compiler::Node* LoadContextElement(compiler::Node* context, int slot_index); compiler::Node* LoadContextElement(compiler::Node* context, int slot_index);
compiler::Node* StoreContextElement(compiler::Node* context, int slot_index,
compiler::Node* value);
compiler::Node* LoadNativeContext(compiler::Node* context); compiler::Node* LoadNativeContext(compiler::Node* context);
compiler::Node* LoadJSArrayElementsMap(ElementsKind kind, compiler::Node* LoadJSArrayElementsMap(ElementsKind kind,
......
...@@ -185,6 +185,8 @@ enum ContextLookupFlags { ...@@ -185,6 +185,8 @@ enum ContextLookupFlags {
V(MAP_CACHE_INDEX, Object, map_cache) \ V(MAP_CACHE_INDEX, Object, map_cache) \
V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \ V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \
V(STRING_ITERATOR_MAP_INDEX, Map, string_iterator_map) \ V(STRING_ITERATOR_MAP_INDEX, Map, string_iterator_map) \
V(MATH_RANDOM_INDEX_INDEX, Smi, math_random_index) \
V(MATH_RANDOM_CACHE_INDEX, Object, math_random_cache) \
V(MESSAGE_LISTENERS_INDEX, TemplateList, message_listeners) \ V(MESSAGE_LISTENERS_INDEX, TemplateList, message_listeners) \
V(NATIVES_UTILS_OBJECT_INDEX, Object, natives_utils_object) \ V(NATIVES_UTILS_OBJECT_INDEX, Object, natives_utils_object) \
V(NORMALIZED_MAP_CACHE_INDEX, Object, normalized_map_cache) \ V(NORMALIZED_MAP_CACHE_INDEX, Object, normalized_map_cache) \
......
...@@ -780,6 +780,7 @@ Handle<Context> Factory::NewNativeContext() { ...@@ -780,6 +780,7 @@ Handle<Context> Factory::NewNativeContext() {
Handle<Context> context = Handle<Context>::cast(array); Handle<Context> context = Handle<Context>::cast(array);
context->set_native_context(*context); context->set_native_context(*context);
context->set_errors_thrown(Smi::kZero); context->set_errors_thrown(Smi::kZero);
context->set_math_random_index(Smi::kZero);
Handle<WeakCell> weak_cell = NewWeakCell(context); Handle<WeakCell> weak_cell = NewWeakCell(context);
context->set_self_weak_cell(*weak_cell); context->set_self_weak_cell(*weak_cell);
DCHECK(context->IsNativeContext()); DCHECK(context->IsNativeContext());
......
...@@ -14,14 +14,13 @@ var GlobalMap = global.Map; ...@@ -14,14 +14,13 @@ var GlobalMap = global.Map;
var GlobalObject = global.Object; var GlobalObject = global.Object;
var GlobalSet = global.Set; var GlobalSet = global.Set;
var hashCodeSymbol = utils.ImportNow("hash_code_symbol"); var hashCodeSymbol = utils.ImportNow("hash_code_symbol");
var MathRandom; var MathRandom = global.Math.random;
var MapIterator; var MapIterator;
var SetIterator; var SetIterator;
var speciesSymbol = utils.ImportNow("species_symbol"); var speciesSymbol = utils.ImportNow("species_symbol");
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) { utils.Import(function(from) {
MathRandom = from.MathRandom;
MapIterator = from.MapIterator; MapIterator = from.MapIterator;
SetIterator = from.SetIterator; SetIterator = from.SetIterator;
}); });
......
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(function(global, utils) {
"use strict";
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Imports
// The first two slots are reserved to persist PRNG state.
define kRandomNumberStart = 2;
var GlobalMath = global.Math;
var NaN = %GetRootNaN();
var nextRandomIndex = 0;
var randomNumbers = UNDEFINED;
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
//-------------------------------------------------------------------
// ECMA 262 - 15.8.2.14
function MathRandom() {
// While creating a startup snapshot, %GenerateRandomNumbers returns a
// normal array containing a single random number, and has to be called for
// every new random number.
// Otherwise, it returns a pre-populated typed array of random numbers. The
// first two elements are reserved for the PRNG state.
if (nextRandomIndex <= kRandomNumberStart) {
randomNumbers = %GenerateRandomNumbers(randomNumbers);
if (%_IsTypedArray(randomNumbers)) {
nextRandomIndex = %_TypedArrayGetLength(randomNumbers);
} else {
nextRandomIndex = randomNumbers.length;
}
}
return randomNumbers[--nextRandomIndex];
}
// -------------------------------------------------------------------
%AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM);
// Set up non-enumerable functions of the Math object and
// set their names.
utils.InstallFunctions(GlobalMath, DONT_ENUM, [
"random", MathRandom,
]);
%SetForceInlineFlag(MathRandom);
// -------------------------------------------------------------------
// Exports
utils.Export(function(to) {
to.MathRandom = MathRandom;
});
})
...@@ -15,58 +15,49 @@ namespace internal { ...@@ -15,58 +15,49 @@ namespace internal {
RUNTIME_FUNCTION(Runtime_GenerateRandomNumbers) { RUNTIME_FUNCTION(Runtime_GenerateRandomNumbers) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 0);
if (isolate->serializer_enabled()) {
// Random numbers in the snapshot are not really that random. And we cannot Handle<Context> native_context = isolate->native_context();
// return a typed array as it cannot be serialized. To make calling DCHECK_EQ(0, native_context->math_random_index()->value());
// Math.random possible when creating a custom startup snapshot, we simply
// return a normal array with a single random number. static const int kCacheSize = 64;
Handle<HeapNumber> random_number = isolate->factory()->NewHeapNumber( static const int kState0Offset = kCacheSize - 1;
isolate->random_number_generator()->NextDouble()); static const int kState1Offset = kState0Offset - 1;
Handle<FixedArray> array_backing = isolate->factory()->NewFixedArray(1); // The index is decremented before used to access the cache.
array_backing->set(0, *random_number); static const int kInitialIndex = kState1Offset;
return *isolate->factory()->NewJSArrayWithElements(array_backing);
}
static const int kState0Offset = 0; Handle<FixedDoubleArray> cache;
static const int kState1Offset = 1; uint64_t state0 = 0;
static const int kRandomBatchSize = 64; uint64_t state1 = 0;
CONVERT_ARG_HANDLE_CHECKED(Object, maybe_typed_array, 0); if (native_context->math_random_cache()->IsFixedDoubleArray()) {
Handle<JSTypedArray> typed_array; cache = Handle<FixedDoubleArray>(
// Allocate typed array if it does not yet exist. FixedDoubleArray::cast(native_context->math_random_cache()), isolate);
if (maybe_typed_array->IsJSTypedArray()) { state0 = double_to_uint64(cache->get_scalar(kState0Offset));
typed_array = Handle<JSTypedArray>::cast(maybe_typed_array); state1 = double_to_uint64(cache->get_scalar(kState1Offset));
} else { } else {
static const int kByteLength = kRandomBatchSize * kDoubleSize; cache = Handle<FixedDoubleArray>::cast(
Handle<JSArrayBuffer> buffer = isolate->factory()->NewFixedDoubleArray(kCacheSize, TENURED));
isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED); native_context->set_math_random_cache(*cache);
JSArrayBuffer::SetupAllocatingData(buffer, isolate, kByteLength, true, // Initialize state if not yet initialized.
SharedFlag::kNotShared); while (state0 == 0 || state1 == 0) {
typed_array = isolate->factory()->NewJSTypedArray( isolate->random_number_generator()->NextBytes(&state0, sizeof(state0));
kExternalFloat64Array, buffer, 0, kRandomBatchSize); isolate->random_number_generator()->NextBytes(&state1, sizeof(state1));
}
} }
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
double* array = FixedDoubleArray* raw_cache = *cache;
reinterpret_cast<double*>(typed_array->GetBuffer()->backing_store());
// Fetch existing state.
uint64_t state0 = double_to_uint64(array[kState0Offset]);
uint64_t state1 = double_to_uint64(array[kState1Offset]);
// Initialize state if not yet initialized.
while (state0 == 0 || state1 == 0) {
isolate->random_number_generator()->NextBytes(&state0, sizeof(state0));
isolate->random_number_generator()->NextBytes(&state1, sizeof(state1));
}
// Create random numbers. // Create random numbers.
for (int i = kState1Offset + 1; i < kRandomBatchSize; i++) { for (int i = 0; i < kInitialIndex; i++) {
// Generate random numbers using xorshift128+. // Generate random numbers using xorshift128+.
base::RandomNumberGenerator::XorShift128(&state0, &state1); base::RandomNumberGenerator::XorShift128(&state0, &state1);
array[i] = base::RandomNumberGenerator::ToDouble(state0, state1); raw_cache->set(i, base::RandomNumberGenerator::ToDouble(state0, state1));
} }
// Persist current state. // Persist current state.
array[kState0Offset] = uint64_to_double(state0); raw_cache->set(kState0Offset, uint64_to_double(state0));
array[kState1Offset] = uint64_to_double(state1); raw_cache->set(kState1Offset, uint64_to_double(state1));
return *typed_array; return Smi::FromInt(kInitialIndex);
} }
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -351,7 +351,7 @@ namespace internal { ...@@ -351,7 +351,7 @@ namespace internal {
F(LiveEditCompareStrings, 2, 1) \ F(LiveEditCompareStrings, 2, 1) \
F(LiveEditRestartFrame, 2, 1) F(LiveEditRestartFrame, 2, 1)
#define FOR_EACH_INTRINSIC_MATHS(F) F(GenerateRandomNumbers, 1, 1) #define FOR_EACH_INTRINSIC_MATHS(F) F(GenerateRandomNumbers, 0, 1)
#define FOR_EACH_INTRINSIC_NUMBERS(F) \ #define FOR_EACH_INTRINSIC_NUMBERS(F) \
F(IsValidSmi, 1, 1) \ F(IsValidSmi, 1, 1) \
......
...@@ -33,6 +33,9 @@ void PartialSerializer::Serialize(Object** o) { ...@@ -33,6 +33,9 @@ void PartialSerializer::Serialize(Object** o) {
context->set(Context::NEXT_CONTEXT_LINK, context->set(Context::NEXT_CONTEXT_LINK,
isolate_->heap()->undefined_value()); isolate_->heap()->undefined_value());
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate())); DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
// Reset math random cache to get fresh random numbers.
context->set_math_random_index(Smi::kZero);
context->set_math_random_cache(isolate_->heap()->undefined_value());
} }
} }
VisitPointer(o); VisitPointer(o);
......
...@@ -2220,7 +2220,6 @@ ...@@ -2220,7 +2220,6 @@
'js/symbol.js', 'js/symbol.js',
'js/array.js', 'js/array.js',
'js/string.js', 'js/string.js',
'js/math.js',
'js/regexp.js', 'js/regexp.js',
'js/arraybuffer.js', 'js/arraybuffer.js',
'js/typedarray.js', 'js/typedarray.js',
......
...@@ -78,7 +78,7 @@ bytecodes: [ ...@@ -78,7 +78,7 @@ bytecodes: [
/* 15 S> */ B(LdrUndefined), R(0), /* 15 S> */ B(LdrUndefined), R(0),
B(CreateArrayLiteral), U8(0), U8(0), U8(9), B(CreateArrayLiteral), U8(0), U8(0), U8(9),
B(Star), R(1), B(Star), R(1),
B(CallJSRuntime), U8(143), R(0), U8(2), B(CallJSRuntime), U8(145), R(0), U8(2),
/* 44 S> */ B(Return), /* 44 S> */ B(Return),
] ]
constant pool: [ constant pool: [
......
...@@ -80,9 +80,9 @@ assertTrue(extension_count == 2 || extension_count == 3); ...@@ -80,9 +80,9 @@ assertTrue(extension_count == 2 || extension_count == 3);
assertTrue(normal_count == 2 || normal_count == 3); assertTrue(normal_count == 2 || normal_count == 3);
// Test a builtins script. // Test a builtins script.
var math_script = Debug.findScript('native math.js'); var array_script = Debug.findScript('native array.js');
assertEquals('native math.js', math_script.name); assertEquals('native array.js', array_script.name);
assertEquals(Debug.ScriptType.Native, math_script.type); assertEquals(Debug.ScriptType.Native, array_script.type);
// Test a debugger script. // Test a debugger script.
var debug_delay_script = Debug.findScript('native debug.js'); var debug_delay_script = Debug.findScript('native debug.js');
......
...@@ -83,7 +83,6 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type, ...@@ -83,7 +83,6 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type,
// Test the script mirror for different functions. // Test the script mirror for different functions.
testScriptMirror(function(){}, 'mirror-script.js', 90, 2, 0); testScriptMirror(function(){}, 'mirror-script.js', 89, 2, 0);
testScriptMirror(Math.random, 'native math.js', -1, 0, 0); testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 86);
testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 87); testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 87);
testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 88);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment