builtins-math-gen.cc 16.6 KB
Newer Older
1 2 3 4
// Copyright 2017 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.

5 6
#include "src/builtins/builtins-math-gen.h"

7 8 9 10 11 12 13 14 15 16 17
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"

namespace v8 {
namespace internal {

// -----------------------------------------------------------------------------
// ES6 section 20.2.2 Function Properties of the Math Object

18
// ES6 #sec-math.abs
19
TF_BUILTIN(MathAbs, CodeStubAssembler) {
20
  Node* context = Parameter(Descriptor::kContext);
21 22

  // We might need to loop once for ToNumber conversion.
23
  VARIABLE(var_x, MachineRepresentation::kTagged);
24
  Label loop(this, &var_x);
25
  var_x.Bind(Parameter(Descriptor::kX));
26
  Goto(&loop);
27
  BIND(&loop);
28 29 30 31 32 33 34 35
  {
    // Load the current {x} value.
    Node* x = var_x.value();

    // Check if {x} is a Smi or a HeapObject.
    Label if_xissmi(this), if_xisnotsmi(this);
    Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);

36
    BIND(&if_xissmi);
37
    {
38
      Label if_overflow(this, Label::kDeferred), if_notoverflow(this);
39
      Node* pair = nullptr;
40

41 42 43
      // check if support abs function
      if (IsIntPtrAbsWithOverflowSupported()) {
        pair = IntPtrAbsWithOverflow(x);
44 45
        Node* overflow = Projection(1, pair);
        Branch(overflow, &if_overflow, &if_notoverflow);
46 47 48
      } else {
        // Check if {x} is already positive.
        Label if_xispositive(this), if_xisnotpositive(this);
49 50
        BranchIfSmiLessThanOrEqual(SmiConstant(0), x, &if_xispositive,
                                   &if_xisnotpositive);
51

52
        BIND(&if_xispositive);
53 54 55 56
        {
          // Just return the input {x}.
          Return(x);
        }
57

58
        BIND(&if_xisnotpositive);
59
        {
60 61 62 63 64
          // Try to negate the {x} value.
          pair =
              IntPtrSubWithOverflow(IntPtrConstant(0), BitcastTaggedToWord(x));
          Node* overflow = Projection(1, pair);
          Branch(overflow, &if_overflow, &if_notoverflow);
65
        }
66
      }
67

68
      BIND(&if_notoverflow);
69 70 71 72
      {
        // There is a Smi representation for negated {x}.
        Node* result = Projection(0, pair);
        Return(BitcastWordToTagged(result));
73
      }
74

75
      BIND(&if_overflow);
76
      { Return(NumberConstant(0.0 - Smi::kMinValue)); }
77 78
    }

79
    BIND(&if_xisnotsmi);
80 81 82
    {
      // Check if {x} is a HeapNumber.
      Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
83
      Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
84

85
      BIND(&if_xisheapnumber);
86 87 88 89 90 91 92
      {
        Node* x_value = LoadHeapNumberValue(x);
        Node* value = Float64Abs(x_value);
        Node* result = AllocateHeapNumberWithValue(value);
        Return(result);
      }

93
      BIND(&if_xisnotheapnumber);
94 95
      {
        // Need to convert {x} to a Number first.
96
        var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
97 98 99 100 101 102 103
        Goto(&loop);
      }
    }
  }
}

void MathBuiltinsAssembler::MathRoundingOperation(
104 105
    Node* context, Node* x,
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
106
  // We might need to loop once for ToNumber conversion.
107
  VARIABLE(var_x, MachineRepresentation::kTagged, x);
108 109
  Label loop(this, &var_x);
  Goto(&loop);
110
  BIND(&loop);
111 112 113 114 115 116 117 118
  {
    // Load the current {x} value.
    Node* x = var_x.value();

    // Check if {x} is a Smi or a HeapObject.
    Label if_xissmi(this), if_xisnotsmi(this);
    Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);

119
    BIND(&if_xissmi);
120 121 122 123 124
    {
      // Nothing to do when {x} is a Smi.
      Return(x);
    }

125
    BIND(&if_xisnotsmi);
126 127 128
    {
      // Check if {x} is a HeapNumber.
      Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
129
      Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
130

131
      BIND(&if_xisheapnumber);
132 133 134 135 136 137 138
      {
        Node* x_value = LoadHeapNumberValue(x);
        Node* value = (this->*float64op)(x_value);
        Node* result = ChangeFloat64ToTagged(value);
        Return(result);
      }

139
      BIND(&if_xisnotheapnumber);
140 141
      {
        // Need to convert {x} to a Number first.
142
        var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
143 144 145 146 147 148 149
        Goto(&loop);
      }
    }
  }
}

void MathBuiltinsAssembler::MathUnaryOperation(
150 151
    Node* context, Node* x,
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
152 153 154 155 156 157 158
  Node* x_value = TruncateTaggedToFloat64(context, x);
  Node* value = (this->*float64op)(x_value);
  Node* result = AllocateHeapNumberWithValue(value);
  Return(result);
}

void MathBuiltinsAssembler::MathMaxMin(
159
    Node* context, Node* argc,
160 161 162
    TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>,
                                                    SloppyTNode<Float64T>),
    double default_val) {
163
  CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
164
  argc = arguments.GetLength(INTPTR_PARAMETERS);
165

166
  VARIABLE(result, MachineRepresentation::kFloat64);
167 168 169
  result.Bind(Float64Constant(default_val));

  CodeStubAssembler::VariableList vars({&result}, zone());
170
  arguments.ForEach(vars, [=, &result](Node* arg) {
171 172 173 174 175 176 177
    Node* float_value = TruncateTaggedToFloat64(context, arg);
    result.Bind((this->*float64op)(result.value(), float_value));
  });

  arguments.PopAndReturn(ChangeFloat64ToTagged(result.value()));
}

178
// ES6 #sec-math.acos
179
TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
180 181 182
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acos);
183 184
}

185
// ES6 #sec-math.acosh
186
TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
187 188 189
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acosh);
190 191
}

192
// ES6 #sec-math.asin
193
TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
194 195 196
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asin);
197 198
}

199
// ES6 #sec-math.asinh
200
TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
201 202 203
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asinh);
204
}
205 206

// ES6 #sec-math.atan
207
TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
208 209 210
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atan);
211 212
}

213
// ES6 #sec-math.atanh
214
TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
215 216 217
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atanh);
218 219
}

220
// ES6 #sec-math.atan2
221
TF_BUILTIN(MathAtan2, CodeStubAssembler) {
222 223 224
  Node* context = Parameter(Descriptor::kContext);
  Node* y = Parameter(Descriptor::kY);
  Node* x = Parameter(Descriptor::kX);
225 226 227 228 229 230 231 232

  Node* y_value = TruncateTaggedToFloat64(context, y);
  Node* x_value = TruncateTaggedToFloat64(context, x);
  Node* value = Float64Atan2(y_value, x_value);
  Node* result = AllocateHeapNumberWithValue(value);
  Return(result);
}

233
// ES6 #sec-math.ceil
234
TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
235 236 237
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Ceil);
238 239
}

240
// ES6 #sec-math.cbrt
241
TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
242 243 244
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cbrt);
245 246
}

247
// ES6 #sec-math.clz32
248
TF_BUILTIN(MathClz32, CodeStubAssembler) {
249
  Node* context = Parameter(Descriptor::kContext);
250 251

  // Shared entry point for the clz32 operation.
252
  VARIABLE(var_clz32_x, MachineRepresentation::kWord32);
253 254 255
  Label do_clz32(this);

  // We might need to loop once for ToNumber conversion.
256
  VARIABLE(var_x, MachineRepresentation::kTagged);
257
  Label loop(this, &var_x);
258
  var_x.Bind(Parameter(Descriptor::kX));
259
  Goto(&loop);
260
  BIND(&loop);
261 262 263 264 265 266 267 268
  {
    // Load the current {x} value.
    Node* x = var_x.value();

    // Check if {x} is a Smi or a HeapObject.
    Label if_xissmi(this), if_xisnotsmi(this);
    Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);

269
    BIND(&if_xissmi);
270
    {
271
      var_clz32_x.Bind(SmiToInt32(x));
272 273 274
      Goto(&do_clz32);
    }

275
    BIND(&if_xisnotsmi);
276 277 278
    {
      // Check if {x} is a HeapNumber.
      Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
279
      Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
280

281
      BIND(&if_xisheapnumber);
282 283 284 285 286
      {
        var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x));
        Goto(&do_clz32);
      }

287
      BIND(&if_xisnotheapnumber);
288 289
      {
        // Need to convert {x} to a Number first.
290
        var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
291 292 293 294 295
        Goto(&loop);
      }
    }
  }

296
  BIND(&do_clz32);
297 298 299 300 301 302 303 304
  {
    Node* x_value = var_clz32_x.value();
    Node* value = Word32Clz(x_value);
    Node* result = ChangeInt32ToTagged(value);
    Return(result);
  }
}

305
// ES6 #sec-math.cos
306
TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
307 308 309
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cos);
310 311
}

312
// ES6 #sec-math.cosh
313
TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
314 315 316
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cosh);
317 318
}

319
// ES6 #sec-math.exp
320
TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
321 322 323
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Exp);
324 325
}

326
// ES6 #sec-math.expm1
327
TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
328 329 330
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Expm1);
331 332
}

333
// ES6 #sec-math.floor
334
TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
335 336 337
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Floor);
338 339
}

340
// ES6 #sec-math.fround
341
TF_BUILTIN(MathFround, CodeStubAssembler) {
342 343
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
344 345 346 347 348 349 350
  Node* x_value = TruncateTaggedToFloat64(context, x);
  Node* value32 = TruncateFloat64ToFloat32(x_value);
  Node* value = ChangeFloat32ToFloat64(value32);
  Node* result = AllocateHeapNumberWithValue(value);
  Return(result);
}

351
// ES6 #sec-math.imul
352
TF_BUILTIN(MathImul, CodeStubAssembler) {
353 354 355
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  Node* y = Parameter(Descriptor::kY);
356 357 358 359 360 361 362
  Node* x_value = TruncateTaggedToWord32(context, x);
  Node* y_value = TruncateTaggedToWord32(context, y);
  Node* value = Int32Mul(x_value, y_value);
  Node* result = ChangeInt32ToTagged(value);
  Return(result);
}

363
// ES6 #sec-math.log
364
TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
365 366 367
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log);
368 369
}

370
// ES6 #sec-math.log1p
371
TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
372 373 374
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log1p);
375 376
}

377
// ES6 #sec-math.log10
378
TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
379 380 381
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log10);
382 383
}

384
// ES6 #sec-math.log2
385
TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
386 387 388
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log2);
389 390
}

391 392 393 394 395 396 397 398 399
CodeStubAssembler::Node* MathBuiltinsAssembler::MathPow(Node* context,
                                                        Node* base,
                                                        Node* exponent) {
  Node* base_value = TruncateTaggedToFloat64(context, base);
  Node* exponent_value = TruncateTaggedToFloat64(context, exponent);
  Node* value = Float64Pow(base_value, exponent_value);
  return ChangeFloat64ToTagged(value);
}

400
// ES6 #sec-math.pow
401 402 403
TF_BUILTIN(MathPow, MathBuiltinsAssembler) {
  Return(MathPow(Parameter(Descriptor::kContext), Parameter(Descriptor::kBase),
                 Parameter(Descriptor::kExponent)));
404 405
}

406
// ES6 #sec-math.random
407
TF_BUILTIN(MathRandom, CodeStubAssembler) {
408
  Node* context = Parameter(Descriptor::kContext);
409 410 411
  Node* native_context = LoadNativeContext(context);

  // Load cache index.
412
  VARIABLE(smi_index, MachineRepresentation::kTagged);
413 414 415 416 417
  smi_index.Bind(
      LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));

  // Cached random numbers are exhausted if index is 0. Go to slow path.
  Label if_cached(this);
418
  GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached);
419 420 421 422 423 424

  // Cache exhausted, populate the cache. Return value is the new index.
  smi_index.Bind(CallRuntime(Runtime::kGenerateRandomNumbers, context));
  Goto(&if_cached);

  // Compute next index by decrement.
425
  BIND(&if_cached);
426
  Node* new_smi_index = SmiSub(smi_index.value(), SmiConstant(1));
427 428 429 430 431 432 433 434 435 436 437
  StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
                      new_smi_index);

  // Load and return next cached random number.
  Node* array =
      LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
  Node* random = LoadFixedDoubleArrayElement(
      array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
  Return(AllocateHeapNumberWithValue(random));
}

438
// ES6 #sec-math.round
439
TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
440 441 442
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Round);
443 444
}

445
// ES6 #sec-math.sign
446 447
TF_BUILTIN(MathSign, CodeStubAssembler) {
  // Convert the {x} value to a Number.
448 449
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
450 451 452 453 454 455 456 457
  Node* x_value = TruncateTaggedToFloat64(context, x);

  // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
  Label if_xisnegative(this), if_xispositive(this);
  GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative);
  GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive);
  Return(ChangeFloat64ToTagged(x_value));

458
  BIND(&if_xisnegative);
459
  Return(SmiConstant(-1));
460

461
  BIND(&if_xispositive);
462
  Return(SmiConstant(1));
463 464
}

465
// ES6 #sec-math.sin
466
TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
467 468 469
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sin);
470 471
}

472
// ES6 #sec-math.sinh
473
TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
474 475 476
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sinh);
477 478
}

479
// ES6 #sec-math.sqrt
480
TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
481 482 483
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sqrt);
484 485
}

486
// ES6 #sec-math.tan
487
TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
488 489 490
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tan);
491 492
}

493
// ES6 #sec-math.tanh
494
TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
495 496 497
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tanh);
498 499
}

500
// ES6 #sec-math.trunc
501
TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
502 503 504
  Node* context = Parameter(Descriptor::kContext);
  Node* x = Parameter(Descriptor::kX);
  MathRoundingOperation(context, x, &CodeStubAssembler::Float64Trunc);
505 506
}

507
// ES6 #sec-math.max
508
TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
509 510 511 512 513
  // TODO(ishell): use constants from Descriptor once the JSFunction linkage
  // arguments are reordered.
  Node* context = Parameter(BuiltinDescriptor::kContext);
  Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
  MathMaxMin(context, argc, &CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
514 515
}

516
// ES6 #sec-math.min
517
TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
518 519 520 521 522
  // TODO(ishell): use constants from Descriptor once the JSFunction linkage
  // arguments are reordered.
  Node* context = Parameter(BuiltinDescriptor::kContext);
  Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
  MathMaxMin(context, argc, &CodeStubAssembler::Float64Min, V8_INFINITY);
523 524 525 526
}

}  // namespace internal
}  // namespace v8