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