Home | History | Annotate | Download | only in builtins
      1 // Copyright 2017 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/builtins/builtins-math-gen.h"
      6 
      7 #include "src/builtins/builtins-utils-gen.h"
      8 #include "src/builtins/builtins.h"
      9 #include "src/code-factory.h"
     10 #include "src/code-stub-assembler.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 // -----------------------------------------------------------------------------
     16 // ES6 section 20.2.2 Function Properties of the Math Object
     17 
     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     // Load the current {x} value.
     30     Node* x = var_x.value();
     31 
     32     // Check if {x} is a Smi or a HeapObject.
     33     Label if_xissmi(this), if_xisnotsmi(this);
     34     Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
     35 
     36     BIND(&if_xissmi);
     37     {
     38       Label if_overflow(this, Label::kDeferred);
     39 
     40       // check if support abs function
     41       if (IsIntPtrAbsWithOverflowSupported()) {
     42         Node* pair = IntPtrAbsWithOverflow(x);
     43         Node* overflow = Projection(1, pair);
     44         GotoIf(overflow, &if_overflow);
     45 
     46         // There is a Smi representation for negated {x}.
     47         Node* result = Projection(0, pair);
     48         Return(BitcastWordToTagged(result));
     49 
     50       } else {
     51         // Check if {x} is already positive.
     52         Label if_xispositive(this), if_xisnotpositive(this);
     53         BranchIfSmiLessThanOrEqual(SmiConstant(0), CAST(x), &if_xispositive,
     54                                    &if_xisnotpositive);
     55 
     56         BIND(&if_xispositive);
     57         {
     58           // Just return the input {x}.
     59           Return(x);
     60         }
     61 
     62         BIND(&if_xisnotpositive);
     63         {
     64           // Try to negate the {x} value.
     65           TNode<Smi> result = TrySmiSub(SmiConstant(0), CAST(x), &if_overflow);
     66           Return(result);
     67         }
     68       }
     69 
     70       BIND(&if_overflow);
     71       { Return(NumberConstant(0.0 - Smi::kMinValue)); }
     72     }
     73 
     74     BIND(&if_xisnotsmi);
     75     {
     76       // Check if {x} is a HeapNumber.
     77       Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
     78       Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
     79 
     80       BIND(&if_xisheapnumber);
     81       {
     82         Node* x_value = LoadHeapNumberValue(x);
     83         Node* value = Float64Abs(x_value);
     84         Node* result = AllocateHeapNumberWithValue(value);
     85         Return(result);
     86       }
     87 
     88       BIND(&if_xisnotheapnumber);
     89       {
     90         // Need to convert {x} to a Number first.
     91         var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
     92         Goto(&loop);
     93       }
     94     }
     95   }
     96 }
     97 
     98 void MathBuiltinsAssembler::MathRoundingOperation(
     99     Node* context, Node* x,
    100     TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
    101   // We might need to loop once for ToNumber conversion.
    102   VARIABLE(var_x, MachineRepresentation::kTagged, x);
    103   Label loop(this, &var_x);
    104   Goto(&loop);
    105   BIND(&loop);
    106   {
    107     // Load the current {x} value.
    108     Node* x = var_x.value();
    109 
    110     // Check if {x} is a Smi or a HeapObject.
    111     Label if_xissmi(this), if_xisnotsmi(this);
    112     Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
    113 
    114     BIND(&if_xissmi);
    115     {
    116       // Nothing to do when {x} is a Smi.
    117       Return(x);
    118     }
    119 
    120     BIND(&if_xisnotsmi);
    121     {
    122       // Check if {x} is a HeapNumber.
    123       Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
    124       Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
    125 
    126       BIND(&if_xisheapnumber);
    127       {
    128         Node* x_value = LoadHeapNumberValue(x);
    129         Node* value = (this->*float64op)(x_value);
    130         Node* result = ChangeFloat64ToTagged(value);
    131         Return(result);
    132       }
    133 
    134       BIND(&if_xisnotheapnumber);
    135       {
    136         // Need to convert {x} to a Number first.
    137         var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
    138         Goto(&loop);
    139       }
    140     }
    141   }
    142 }
    143 
    144 void MathBuiltinsAssembler::MathUnaryOperation(
    145     Node* context, Node* x,
    146     TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>)) {
    147   Node* x_value = TruncateTaggedToFloat64(context, x);
    148   Node* value = (this->*float64op)(x_value);
    149   Node* result = AllocateHeapNumberWithValue(value);
    150   Return(result);
    151 }
    152 
    153 void MathBuiltinsAssembler::MathMaxMin(
    154     Node* context, Node* argc,
    155     TNode<Float64T> (CodeStubAssembler::*float64op)(SloppyTNode<Float64T>,
    156                                                     SloppyTNode<Float64T>),
    157     double default_val) {
    158   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
    159   argc = arguments.GetLength(INTPTR_PARAMETERS);
    160 
    161   VARIABLE(result, MachineRepresentation::kFloat64);
    162   result.Bind(Float64Constant(default_val));
    163 
    164   CodeStubAssembler::VariableList vars({&result}, zone());
    165   arguments.ForEach(vars, [=, &result](Node* arg) {
    166     Node* float_value = TruncateTaggedToFloat64(context, arg);
    167     result.Bind((this->*float64op)(result.value(), float_value));
    168   });
    169 
    170   arguments.PopAndReturn(ChangeFloat64ToTagged(result.value()));
    171 }
    172 
    173 // ES6 #sec-math.acos
    174 TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
    175   Node* context = Parameter(Descriptor::kContext);
    176   Node* x = Parameter(Descriptor::kX);
    177   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acos);
    178 }
    179 
    180 // ES6 #sec-math.acosh
    181 TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
    182   Node* context = Parameter(Descriptor::kContext);
    183   Node* x = Parameter(Descriptor::kX);
    184   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Acosh);
    185 }
    186 
    187 // ES6 #sec-math.asin
    188 TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
    189   Node* context = Parameter(Descriptor::kContext);
    190   Node* x = Parameter(Descriptor::kX);
    191   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asin);
    192 }
    193 
    194 // ES6 #sec-math.asinh
    195 TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
    196   Node* context = Parameter(Descriptor::kContext);
    197   Node* x = Parameter(Descriptor::kX);
    198   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Asinh);
    199 }
    200 
    201 // ES6 #sec-math.atan
    202 TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
    203   Node* context = Parameter(Descriptor::kContext);
    204   Node* x = Parameter(Descriptor::kX);
    205   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atan);
    206 }
    207 
    208 // ES6 #sec-math.atanh
    209 TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
    210   Node* context = Parameter(Descriptor::kContext);
    211   Node* x = Parameter(Descriptor::kX);
    212   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Atanh);
    213 }
    214 
    215 // ES6 #sec-math.atan2
    216 TF_BUILTIN(MathAtan2, CodeStubAssembler) {
    217   Node* context = Parameter(Descriptor::kContext);
    218   Node* y = Parameter(Descriptor::kY);
    219   Node* x = Parameter(Descriptor::kX);
    220 
    221   Node* y_value = TruncateTaggedToFloat64(context, y);
    222   Node* x_value = TruncateTaggedToFloat64(context, x);
    223   Node* value = Float64Atan2(y_value, x_value);
    224   Node* result = AllocateHeapNumberWithValue(value);
    225   Return(result);
    226 }
    227 
    228 // ES6 #sec-math.ceil
    229 TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
    230   Node* context = Parameter(Descriptor::kContext);
    231   Node* x = Parameter(Descriptor::kX);
    232   MathRoundingOperation(context, x, &CodeStubAssembler::Float64Ceil);
    233 }
    234 
    235 // ES6 #sec-math.cbrt
    236 TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
    237   Node* context = Parameter(Descriptor::kContext);
    238   Node* x = Parameter(Descriptor::kX);
    239   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cbrt);
    240 }
    241 
    242 // ES6 #sec-math.clz32
    243 TF_BUILTIN(MathClz32, CodeStubAssembler) {
    244   Node* context = Parameter(Descriptor::kContext);
    245 
    246   // Shared entry point for the clz32 operation.
    247   VARIABLE(var_clz32_x, MachineRepresentation::kWord32);
    248   Label do_clz32(this);
    249 
    250   // We might need to loop once for ToNumber conversion.
    251   VARIABLE(var_x, MachineRepresentation::kTagged);
    252   Label loop(this, &var_x);
    253   var_x.Bind(Parameter(Descriptor::kX));
    254   Goto(&loop);
    255   BIND(&loop);
    256   {
    257     // Load the current {x} value.
    258     Node* x = var_x.value();
    259 
    260     // Check if {x} is a Smi or a HeapObject.
    261     Label if_xissmi(this), if_xisnotsmi(this);
    262     Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
    263 
    264     BIND(&if_xissmi);
    265     {
    266       var_clz32_x.Bind(SmiToInt32(x));
    267       Goto(&do_clz32);
    268     }
    269 
    270     BIND(&if_xisnotsmi);
    271     {
    272       // Check if {x} is a HeapNumber.
    273       Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
    274       Branch(IsHeapNumber(x), &if_xisheapnumber, &if_xisnotheapnumber);
    275 
    276       BIND(&if_xisheapnumber);
    277       {
    278         var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x));
    279         Goto(&do_clz32);
    280       }
    281 
    282       BIND(&if_xisnotheapnumber);
    283       {
    284         // Need to convert {x} to a Number first.
    285         var_x.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, x));
    286         Goto(&loop);
    287       }
    288     }
    289   }
    290 
    291   BIND(&do_clz32);
    292   {
    293     Node* x_value = var_clz32_x.value();
    294     Node* value = Word32Clz(x_value);
    295     Node* result = ChangeInt32ToTagged(value);
    296     Return(result);
    297   }
    298 }
    299 
    300 // ES6 #sec-math.cos
    301 TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
    302   Node* context = Parameter(Descriptor::kContext);
    303   Node* x = Parameter(Descriptor::kX);
    304   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cos);
    305 }
    306 
    307 // ES6 #sec-math.cosh
    308 TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
    309   Node* context = Parameter(Descriptor::kContext);
    310   Node* x = Parameter(Descriptor::kX);
    311   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Cosh);
    312 }
    313 
    314 // ES6 #sec-math.exp
    315 TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
    316   Node* context = Parameter(Descriptor::kContext);
    317   Node* x = Parameter(Descriptor::kX);
    318   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Exp);
    319 }
    320 
    321 // ES6 #sec-math.expm1
    322 TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
    323   Node* context = Parameter(Descriptor::kContext);
    324   Node* x = Parameter(Descriptor::kX);
    325   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Expm1);
    326 }
    327 
    328 // ES6 #sec-math.floor
    329 TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
    330   Node* context = Parameter(Descriptor::kContext);
    331   Node* x = Parameter(Descriptor::kX);
    332   MathRoundingOperation(context, x, &CodeStubAssembler::Float64Floor);
    333 }
    334 
    335 // ES6 #sec-math.fround
    336 TF_BUILTIN(MathFround, CodeStubAssembler) {
    337   Node* context = Parameter(Descriptor::kContext);
    338   Node* x = Parameter(Descriptor::kX);
    339   Node* x_value = TruncateTaggedToFloat64(context, x);
    340   Node* value32 = TruncateFloat64ToFloat32(x_value);
    341   Node* value = ChangeFloat32ToFloat64(value32);
    342   Node* result = AllocateHeapNumberWithValue(value);
    343   Return(result);
    344 }
    345 
    346 // ES6 #sec-math.imul
    347 TF_BUILTIN(MathImul, CodeStubAssembler) {
    348   Node* context = Parameter(Descriptor::kContext);
    349   Node* x = Parameter(Descriptor::kX);
    350   Node* y = Parameter(Descriptor::kY);
    351   Node* x_value = TruncateTaggedToWord32(context, x);
    352   Node* y_value = TruncateTaggedToWord32(context, y);
    353   Node* value = Int32Mul(x_value, y_value);
    354   Node* result = ChangeInt32ToTagged(value);
    355   Return(result);
    356 }
    357 
    358 // ES6 #sec-math.log
    359 TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
    360   Node* context = Parameter(Descriptor::kContext);
    361   Node* x = Parameter(Descriptor::kX);
    362   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log);
    363 }
    364 
    365 // ES6 #sec-math.log1p
    366 TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
    367   Node* context = Parameter(Descriptor::kContext);
    368   Node* x = Parameter(Descriptor::kX);
    369   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log1p);
    370 }
    371 
    372 // ES6 #sec-math.log10
    373 TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
    374   Node* context = Parameter(Descriptor::kContext);
    375   Node* x = Parameter(Descriptor::kX);
    376   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log10);
    377 }
    378 
    379 // ES6 #sec-math.log2
    380 TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
    381   Node* context = Parameter(Descriptor::kContext);
    382   Node* x = Parameter(Descriptor::kX);
    383   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Log2);
    384 }
    385 
    386 CodeStubAssembler::Node* MathBuiltinsAssembler::MathPow(Node* context,
    387                                                         Node* base,
    388                                                         Node* exponent) {
    389   Node* base_value = TruncateTaggedToFloat64(context, base);
    390   Node* exponent_value = TruncateTaggedToFloat64(context, exponent);
    391   Node* value = Float64Pow(base_value, exponent_value);
    392   return ChangeFloat64ToTagged(value);
    393 }
    394 
    395 // ES6 #sec-math.pow
    396 TF_BUILTIN(MathPow, MathBuiltinsAssembler) {
    397   Return(MathPow(Parameter(Descriptor::kContext), Parameter(Descriptor::kBase),
    398                  Parameter(Descriptor::kExponent)));
    399 }
    400 
    401 // ES6 #sec-math.random
    402 TF_BUILTIN(MathRandom, CodeStubAssembler) {
    403   Node* context = Parameter(Descriptor::kContext);
    404   Node* native_context = LoadNativeContext(context);
    405 
    406   // Load cache index.
    407   TVARIABLE(Smi, smi_index);
    408   smi_index = CAST(
    409       LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));
    410 
    411   // Cached random numbers are exhausted if index is 0. Go to slow path.
    412   Label if_cached(this);
    413   GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached);
    414 
    415   // Cache exhausted, populate the cache. Return value is the new index.
    416   smi_index = CAST(CallRuntime(Runtime::kGenerateRandomNumbers, context));
    417   Goto(&if_cached);
    418 
    419   // Compute next index by decrement.
    420   BIND(&if_cached);
    421   TNode<Smi> new_smi_index = SmiSub(smi_index.value(), SmiConstant(1));
    422   StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
    423                       new_smi_index);
    424 
    425   // Load and return next cached random number.
    426   Node* array =
    427       LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
    428   Node* random = LoadFixedDoubleArrayElement(
    429       array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
    430   Return(AllocateHeapNumberWithValue(random));
    431 }
    432 
    433 // ES6 #sec-math.round
    434 TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
    435   Node* context = Parameter(Descriptor::kContext);
    436   Node* x = Parameter(Descriptor::kX);
    437   MathRoundingOperation(context, x, &CodeStubAssembler::Float64Round);
    438 }
    439 
    440 // ES6 #sec-math.sign
    441 TF_BUILTIN(MathSign, CodeStubAssembler) {
    442   // Convert the {x} value to a Number.
    443   Node* context = Parameter(Descriptor::kContext);
    444   Node* x = Parameter(Descriptor::kX);
    445   Node* x_value = TruncateTaggedToFloat64(context, x);
    446 
    447   // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
    448   Label if_xisnegative(this), if_xispositive(this);
    449   GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative);
    450   GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive);
    451   Return(ChangeFloat64ToTagged(x_value));
    452 
    453   BIND(&if_xisnegative);
    454   Return(SmiConstant(-1));
    455 
    456   BIND(&if_xispositive);
    457   Return(SmiConstant(1));
    458 }
    459 
    460 // ES6 #sec-math.sin
    461 TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
    462   Node* context = Parameter(Descriptor::kContext);
    463   Node* x = Parameter(Descriptor::kX);
    464   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sin);
    465 }
    466 
    467 // ES6 #sec-math.sinh
    468 TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
    469   Node* context = Parameter(Descriptor::kContext);
    470   Node* x = Parameter(Descriptor::kX);
    471   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sinh);
    472 }
    473 
    474 // ES6 #sec-math.sqrt
    475 TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
    476   Node* context = Parameter(Descriptor::kContext);
    477   Node* x = Parameter(Descriptor::kX);
    478   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Sqrt);
    479 }
    480 
    481 // ES6 #sec-math.tan
    482 TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
    483   Node* context = Parameter(Descriptor::kContext);
    484   Node* x = Parameter(Descriptor::kX);
    485   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tan);
    486 }
    487 
    488 // ES6 #sec-math.tanh
    489 TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
    490   Node* context = Parameter(Descriptor::kContext);
    491   Node* x = Parameter(Descriptor::kX);
    492   MathUnaryOperation(context, x, &CodeStubAssembler::Float64Tanh);
    493 }
    494 
    495 // ES6 #sec-math.trunc
    496 TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
    497   Node* context = Parameter(Descriptor::kContext);
    498   Node* x = Parameter(Descriptor::kX);
    499   MathRoundingOperation(context, x, &CodeStubAssembler::Float64Trunc);
    500 }
    501 
    502 // ES6 #sec-math.max
    503 TF_BUILTIN(MathMax, MathBuiltinsAssembler) {
    504   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
    505   // arguments are reordered.
    506   Node* context = Parameter(Descriptor::kContext);
    507   Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
    508   MathMaxMin(context, argc, &CodeStubAssembler::Float64Max, -1.0 * V8_INFINITY);
    509 }
    510 
    511 // ES6 #sec-math.min
    512 TF_BUILTIN(MathMin, MathBuiltinsAssembler) {
    513   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
    514   // arguments are reordered.
    515   Node* context = Parameter(Descriptor::kContext);
    516   Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);
    517   MathMaxMin(context, argc, &CodeStubAssembler::Float64Min, V8_INFINITY);
    518 }
    519 
    520 }  // namespace internal
    521 }  // namespace v8
    522