[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}
This commit is contained in:
yangguo 2016-10-10 23:46:56 -07:00 committed by Commit bot
parent 3d5ae0f7d3
commit 0d8e52123e
17 changed files with 104 additions and 115 deletions

View File

@ -398,7 +398,6 @@ action("js2c") {
"src/js/symbol.js",
"src/js/array.js",
"src/js/string.js",
"src/js/math.js",
"src/js/regexp.js",
"src/js/arraybuffer.js",
"src/js/typedarray.js",

View File

@ -1951,6 +1951,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<JSFunction> math_pow =
SimpleInstallFunction(math, "pow", Builtins::kMathPow, 2, true);
native_context()->set_math_pow(*math_pow);
SimpleInstallFunction(math, "random", Builtins::kMathRandom, 0, true);
SimpleInstallFunction(math, "round", Builtins::kMathRound, 1, true);
SimpleInstallFunction(math, "sign", Builtins::kMathSign, 1, true);
SimpleInstallFunction(math, "sin", Builtins::kMathSin, 1, true);
@ -1993,6 +1994,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
math, factory->NewStringFromAsciiChecked("SQRT2"),
factory->NewNumber(std::sqrt(2.0)),
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

View File

@ -452,6 +452,46 @@ void Builtins::Generate_MathPow(CodeStubAssembler* assembler) {
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 )
void Builtins::Generate_MathRound(CodeStubAssembler* assembler) {
Generate_MathRoundingOperation(assembler, &CodeStubAssembler::Float64Round);

View File

@ -450,6 +450,8 @@ namespace internal {
ASM(MathMin) \
/* ES6 section 20.2.2.26 Math.pow ( x, y ) */ \
TFJ(MathPow, 3) \
/* ES6 section 20.2.2.27 Math.random */ \
TFJ(MathRandom, 1) \
/* ES6 section 20.2.2.28 Math.round ( x ) */ \
TFJ(MathRound, 2) \
/* ES6 section 20.2.2.29 Math.sign ( x ) */ \

View File

@ -1092,6 +1092,13 @@ Node* CodeStubAssembler::LoadContextElement(Node* context, int slot_index) {
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) {
return LoadContextElement(context, Context::NATIVE_CONTEXT_INDEX);
}

View File

@ -298,6 +298,8 @@ class CodeStubAssembler : public compiler::CodeAssembler {
// Context manipulation
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* LoadJSArrayElementsMap(ElementsKind kind,

View File

@ -185,6 +185,8 @@ enum ContextLookupFlags {
V(MAP_CACHE_INDEX, Object, map_cache) \
V(MAP_ITERATOR_MAP_INDEX, Map, map_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(NATIVES_UTILS_OBJECT_INDEX, Object, natives_utils_object) \
V(NORMALIZED_MAP_CACHE_INDEX, Object, normalized_map_cache) \

View File

@ -780,6 +780,7 @@ Handle<Context> Factory::NewNativeContext() {
Handle<Context> context = Handle<Context>::cast(array);
context->set_native_context(*context);
context->set_errors_thrown(Smi::kZero);
context->set_math_random_index(Smi::kZero);
Handle<WeakCell> weak_cell = NewWeakCell(context);
context->set_self_weak_cell(*weak_cell);
DCHECK(context->IsNativeContext());

View File

@ -14,14 +14,13 @@ var GlobalMap = global.Map;
var GlobalObject = global.Object;
var GlobalSet = global.Set;
var hashCodeSymbol = utils.ImportNow("hash_code_symbol");
var MathRandom;
var MathRandom = global.Math.random;
var MapIterator;
var SetIterator;
var speciesSymbol = utils.ImportNow("species_symbol");
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) {
MathRandom = from.MathRandom;
MapIterator = from.MapIterator;
SetIterator = from.SetIterator;
});

View File

@ -1,60 +0,0 @@
// 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;
});
})

View File

@ -15,58 +15,49 @@ namespace internal {
RUNTIME_FUNCTION(Runtime_GenerateRandomNumbers) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
if (isolate->serializer_enabled()) {
// Random numbers in the snapshot are not really that random. And we cannot
// return a typed array as it cannot be serialized. To make calling
// Math.random possible when creating a custom startup snapshot, we simply
// return a normal array with a single random number.
Handle<HeapNumber> random_number = isolate->factory()->NewHeapNumber(
isolate->random_number_generator()->NextDouble());
Handle<FixedArray> array_backing = isolate->factory()->NewFixedArray(1);
array_backing->set(0, *random_number);
return *isolate->factory()->NewJSArrayWithElements(array_backing);
}
DCHECK(args.length() == 0);
static const int kState0Offset = 0;
static const int kState1Offset = 1;
static const int kRandomBatchSize = 64;
CONVERT_ARG_HANDLE_CHECKED(Object, maybe_typed_array, 0);
Handle<JSTypedArray> typed_array;
// Allocate typed array if it does not yet exist.
if (maybe_typed_array->IsJSTypedArray()) {
typed_array = Handle<JSTypedArray>::cast(maybe_typed_array);
Handle<Context> native_context = isolate->native_context();
DCHECK_EQ(0, native_context->math_random_index()->value());
static const int kCacheSize = 64;
static const int kState0Offset = kCacheSize - 1;
static const int kState1Offset = kState0Offset - 1;
// The index is decremented before used to access the cache.
static const int kInitialIndex = kState1Offset;
Handle<FixedDoubleArray> cache;
uint64_t state0 = 0;
uint64_t state1 = 0;
if (native_context->math_random_cache()->IsFixedDoubleArray()) {
cache = Handle<FixedDoubleArray>(
FixedDoubleArray::cast(native_context->math_random_cache()), isolate);
state0 = double_to_uint64(cache->get_scalar(kState0Offset));
state1 = double_to_uint64(cache->get_scalar(kState1Offset));
} else {
static const int kByteLength = kRandomBatchSize * kDoubleSize;
Handle<JSArrayBuffer> buffer =
isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
JSArrayBuffer::SetupAllocatingData(buffer, isolate, kByteLength, true,
SharedFlag::kNotShared);
typed_array = isolate->factory()->NewJSTypedArray(
kExternalFloat64Array, buffer, 0, kRandomBatchSize);
cache = Handle<FixedDoubleArray>::cast(
isolate->factory()->NewFixedDoubleArray(kCacheSize, TENURED));
native_context->set_math_random_cache(*cache);
// 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));
}
}
DisallowHeapAllocation no_gc;
double* array =
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));
}
FixedDoubleArray* raw_cache = *cache;
// Create random numbers.
for (int i = kState1Offset + 1; i < kRandomBatchSize; i++) {
for (int i = 0; i < kInitialIndex; i++) {
// Generate random numbers using xorshift128+.
base::RandomNumberGenerator::XorShift128(&state0, &state1);
array[i] = base::RandomNumberGenerator::ToDouble(state0, state1);
raw_cache->set(i, base::RandomNumberGenerator::ToDouble(state0, state1));
}
// Persist current state.
array[kState0Offset] = uint64_to_double(state0);
array[kState1Offset] = uint64_to_double(state1);
return *typed_array;
raw_cache->set(kState0Offset, uint64_to_double(state0));
raw_cache->set(kState1Offset, uint64_to_double(state1));
return Smi::FromInt(kInitialIndex);
}
} // namespace internal
} // namespace v8

View File

@ -351,7 +351,7 @@ namespace internal {
F(LiveEditCompareStrings, 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) \
F(IsValidSmi, 1, 1) \

View File

@ -33,6 +33,9 @@ void PartialSerializer::Serialize(Object** o) {
context->set(Context::NEXT_CONTEXT_LINK,
isolate_->heap()->undefined_value());
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);

View File

@ -2220,7 +2220,6 @@
'js/symbol.js',
'js/array.js',
'js/string.js',
'js/math.js',
'js/regexp.js',
'js/arraybuffer.js',
'js/typedarray.js',

View File

@ -78,7 +78,7 @@ bytecodes: [
/* 15 S> */ B(LdrUndefined), R(0),
B(CreateArrayLiteral), U8(0), U8(0), U8(9),
B(Star), R(1),
B(CallJSRuntime), U8(143), R(0), U8(2),
B(CallJSRuntime), U8(145), R(0), U8(2),
/* 44 S> */ B(Return),
]
constant pool: [

View File

@ -80,9 +80,9 @@ assertTrue(extension_count == 2 || extension_count == 3);
assertTrue(normal_count == 2 || normal_count == 3);
// Test a builtins script.
var math_script = Debug.findScript('native math.js');
assertEquals('native math.js', math_script.name);
assertEquals(Debug.ScriptType.Native, math_script.type);
var array_script = Debug.findScript('native array.js');
assertEquals('native array.js', array_script.name);
assertEquals(Debug.ScriptType.Native, array_script.type);
// Test a debugger script.
var debug_delay_script = Debug.findScript('native debug.js');

View File

@ -83,7 +83,6 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type,
// Test the script mirror for different functions.
testScriptMirror(function(){}, 'mirror-script.js', 90, 2, 0);
testScriptMirror(Math.random, 'native math.js', -1, 0, 0);
testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 87);
testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 88);
testScriptMirror(function(){}, 'mirror-script.js', 89, 2, 0);
testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 86);
testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 87);