runtime-maths.cc revision 109988c7ccb6f3fd1a58574fa3dfb88beaef6632
1// Copyright 2014 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/runtime/runtime-utils.h" 6 7#include "src/arguments.h" 8#include "src/assembler.h" 9#include "src/base/utils/random-number-generator.h" 10#include "src/bootstrapper.h" 11#include "src/codegen.h" 12#include "src/third_party/fdlibm/fdlibm.h" 13 14namespace v8 { 15namespace internal { 16 17#define RUNTIME_UNARY_MATH(Name, name) \ 18 RUNTIME_FUNCTION(Runtime_Math##Name) { \ 19 HandleScope scope(isolate); \ 20 DCHECK(args.length() == 1); \ 21 isolate->counters()->math_##name##_runtime()->Increment(); \ 22 CONVERT_DOUBLE_ARG_CHECKED(x, 0); \ 23 return *isolate->factory()->NewHeapNumber(std::name(x)); \ 24 } 25 26RUNTIME_UNARY_MATH(Acos, acos) 27RUNTIME_UNARY_MATH(Asin, asin) 28RUNTIME_UNARY_MATH(Atan, atan) 29RUNTIME_UNARY_MATH(LogRT, log) 30#undef RUNTIME_UNARY_MATH 31 32 33RUNTIME_FUNCTION(Runtime_DoubleHi) { 34 HandleScope scope(isolate); 35 DCHECK(args.length() == 1); 36 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 37 uint64_t unsigned64 = double_to_uint64(x); 38 uint32_t unsigned32 = static_cast<uint32_t>(unsigned64 >> 32); 39 int32_t signed32 = bit_cast<int32_t, uint32_t>(unsigned32); 40 return *isolate->factory()->NewNumber(signed32); 41} 42 43 44RUNTIME_FUNCTION(Runtime_DoubleLo) { 45 HandleScope scope(isolate); 46 DCHECK(args.length() == 1); 47 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 48 uint64_t unsigned64 = double_to_uint64(x); 49 uint32_t unsigned32 = static_cast<uint32_t>(unsigned64); 50 int32_t signed32 = bit_cast<int32_t, uint32_t>(unsigned32); 51 return *isolate->factory()->NewNumber(signed32); 52} 53 54 55RUNTIME_FUNCTION(Runtime_ConstructDouble) { 56 HandleScope scope(isolate); 57 DCHECK(args.length() == 2); 58 CONVERT_NUMBER_CHECKED(uint32_t, hi, Uint32, args[0]); 59 CONVERT_NUMBER_CHECKED(uint32_t, lo, Uint32, args[1]); 60 uint64_t result = (static_cast<uint64_t>(hi) << 32) | lo; 61 return *isolate->factory()->NewNumber(uint64_to_double(result)); 62} 63 64 65RUNTIME_FUNCTION(Runtime_RemPiO2) { 66 SealHandleScope shs(isolate); 67 DisallowHeapAllocation no_gc; 68 DCHECK(args.length() == 2); 69 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 70 CONVERT_ARG_CHECKED(JSTypedArray, result, 1); 71 RUNTIME_ASSERT(result->byte_length() == Smi::FromInt(2 * sizeof(double))); 72 FixedFloat64Array* array = FixedFloat64Array::cast(result->elements()); 73 double* y = static_cast<double*>(array->DataPtr()); 74 return Smi::FromInt(fdlibm::rempio2(x, y)); 75} 76 77 78static const double kPiDividedBy4 = 0.78539816339744830962; 79 80 81RUNTIME_FUNCTION(Runtime_MathAtan2) { 82 HandleScope scope(isolate); 83 DCHECK(args.length() == 2); 84 isolate->counters()->math_atan2_runtime()->Increment(); 85 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 86 CONVERT_DOUBLE_ARG_CHECKED(y, 1); 87 double result; 88 if (std::isinf(x) && std::isinf(y)) { 89 // Make sure that the result in case of two infinite arguments 90 // is a multiple of Pi / 4. The sign of the result is determined 91 // by the first argument (x) and the sign of the second argument 92 // determines the multiplier: one or three. 93 int multiplier = (x < 0) ? -1 : 1; 94 if (y < 0) multiplier *= 3; 95 result = multiplier * kPiDividedBy4; 96 } else { 97 result = std::atan2(x, y); 98 } 99 return *isolate->factory()->NewNumber(result); 100} 101 102 103RUNTIME_FUNCTION(Runtime_MathExpRT) { 104 HandleScope scope(isolate); 105 DCHECK(args.length() == 1); 106 isolate->counters()->math_exp_runtime()->Increment(); 107 108 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 109 lazily_initialize_fast_exp(isolate); 110 return *isolate->factory()->NewNumber(fast_exp(x, isolate)); 111} 112 113 114RUNTIME_FUNCTION(Runtime_MathClz32) { 115 HandleScope scope(isolate); 116 DCHECK(args.length() == 1); 117 isolate->counters()->math_clz32_runtime()->Increment(); 118 119 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]); 120 return *isolate->factory()->NewNumberFromUint( 121 base::bits::CountLeadingZeros32(x)); 122} 123 124 125RUNTIME_FUNCTION(Runtime_MathFloor) { 126 HandleScope scope(isolate); 127 DCHECK(args.length() == 1); 128 isolate->counters()->math_floor_runtime()->Increment(); 129 130 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 131 return *isolate->factory()->NewNumber(Floor(x)); 132} 133 134 135// Slow version of Math.pow. We check for fast paths for special cases. 136// Used if VFP3 is not available. 137RUNTIME_FUNCTION(Runtime_MathPow) { 138 HandleScope scope(isolate); 139 DCHECK(args.length() == 2); 140 isolate->counters()->math_pow_runtime()->Increment(); 141 142 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 143 144 // If the second argument is a smi, it is much faster to call the 145 // custom powi() function than the generic pow(). 146 if (args[1]->IsSmi()) { 147 int y = args.smi_at(1); 148 return *isolate->factory()->NewNumber(power_double_int(x, y)); 149 } 150 151 CONVERT_DOUBLE_ARG_CHECKED(y, 1); 152 double result = power_helper(isolate, x, y); 153 if (std::isnan(result)) return isolate->heap()->nan_value(); 154 return *isolate->factory()->NewNumber(result); 155} 156 157 158// Fast version of Math.pow if we know that y is not an integer and y is not 159// -0.5 or 0.5. Used as slow case from full codegen. 160RUNTIME_FUNCTION(Runtime_MathPowRT) { 161 HandleScope scope(isolate); 162 DCHECK(args.length() == 2); 163 isolate->counters()->math_pow_runtime()->Increment(); 164 165 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 166 CONVERT_DOUBLE_ARG_CHECKED(y, 1); 167 if (y == 0) { 168 return Smi::FromInt(1); 169 } else { 170 double result = power_double_double(x, y); 171 if (std::isnan(result)) return isolate->heap()->nan_value(); 172 return *isolate->factory()->NewNumber(result); 173 } 174} 175 176 177RUNTIME_FUNCTION(Runtime_RoundNumber) { 178 HandleScope scope(isolate); 179 DCHECK(args.length() == 1); 180 CONVERT_NUMBER_ARG_HANDLE_CHECKED(input, 0); 181 isolate->counters()->math_round_runtime()->Increment(); 182 183 if (!input->IsHeapNumber()) { 184 DCHECK(input->IsSmi()); 185 return *input; 186 } 187 188 Handle<HeapNumber> number = Handle<HeapNumber>::cast(input); 189 190 double value = number->value(); 191 int exponent = number->get_exponent(); 192 int sign = number->get_sign(); 193 194 if (exponent < -1) { 195 // Number in range ]-0.5..0.5[. These always round to +/-zero. 196 if (sign) return isolate->heap()->minus_zero_value(); 197 return Smi::FromInt(0); 198 } 199 200 // We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and 201 // should be rounded to 2^30, which is not smi (for 31-bit smis, similar 202 // argument holds for 32-bit smis). 203 if (!sign && exponent < kSmiValueSize - 2) { 204 return Smi::FromInt(static_cast<int>(value + 0.5)); 205 } 206 207 // If the magnitude is big enough, there's no place for fraction part. If we 208 // try to add 0.5 to this number, 1.0 will be added instead. 209 if (exponent >= 52) { 210 return *number; 211 } 212 213 if (sign && value >= -0.5) return isolate->heap()->minus_zero_value(); 214 215 // Do not call NumberFromDouble() to avoid extra checks. 216 return *isolate->factory()->NewNumber(Floor(value + 0.5)); 217} 218 219 220RUNTIME_FUNCTION(Runtime_MathSqrt) { 221 HandleScope scope(isolate); 222 DCHECK(args.length() == 1); 223 isolate->counters()->math_sqrt_runtime()->Increment(); 224 225 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 226 lazily_initialize_fast_sqrt(isolate); 227 return *isolate->factory()->NewNumber(fast_sqrt(x, isolate)); 228} 229 230 231RUNTIME_FUNCTION(Runtime_MathFround) { 232 HandleScope scope(isolate); 233 DCHECK(args.length() == 1); 234 235 CONVERT_DOUBLE_ARG_CHECKED(x, 0); 236 float xf = DoubleToFloat32(x); 237 return *isolate->factory()->NewNumber(xf); 238} 239 240 241RUNTIME_FUNCTION(Runtime_GenerateRandomNumbers) { 242 HandleScope scope(isolate); 243 DCHECK(args.length() == 1); 244 // Random numbers in the snapshot are not really that random. 245 DCHECK(!isolate->bootstrapper()->IsActive()); 246 static const int kState0Offset = 0; 247 static const int kState1Offset = 1; 248 static const int kRandomBatchSize = 64; 249 CONVERT_ARG_HANDLE_CHECKED(Object, maybe_typed_array, 0); 250 Handle<JSTypedArray> typed_array; 251 // Allocate typed array if it does not yet exist. 252 if (maybe_typed_array->IsJSTypedArray()) { 253 typed_array = Handle<JSTypedArray>::cast(maybe_typed_array); 254 } else { 255 static const int kByteLength = kRandomBatchSize * kDoubleSize; 256 Handle<JSArrayBuffer> buffer = 257 isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED); 258 JSArrayBuffer::SetupAllocatingData(buffer, isolate, kByteLength, true, 259 SharedFlag::kNotShared); 260 typed_array = isolate->factory()->NewJSTypedArray( 261 kExternalFloat64Array, buffer, 0, kRandomBatchSize); 262 } 263 264 DisallowHeapAllocation no_gc; 265 double* array = 266 reinterpret_cast<double*>(typed_array->GetBuffer()->backing_store()); 267 // Fetch existing state. 268 uint64_t state0 = double_to_uint64(array[kState0Offset]); 269 uint64_t state1 = double_to_uint64(array[kState1Offset]); 270 // Initialize state if not yet initialized. 271 while (state0 == 0 || state1 == 0) { 272 isolate->random_number_generator()->NextBytes(&state0, sizeof(state0)); 273 isolate->random_number_generator()->NextBytes(&state1, sizeof(state1)); 274 } 275 // Create random numbers. 276 for (int i = kState1Offset + 1; i < kRandomBatchSize; i++) { 277 // Generate random numbers using xorshift128+. 278 base::RandomNumberGenerator::XorShift128(&state0, &state1); 279 array[i] = base::RandomNumberGenerator::ToDouble(state0, state1); 280 } 281 // Persist current state. 282 array[kState0Offset] = uint64_to_double(state0); 283 array[kState1Offset] = uint64_to_double(state1); 284 return *typed_array; 285} 286} // namespace internal 287} // namespace v8 288