Home | History | Annotate | Download | only in builtins
      1 // Copyright 2016 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-utils.h"
      6 #include "src/builtins/builtins.h"
      7 #include "src/code-factory.h"
      8 #include "src/code-stub-assembler.h"
      9 #include "src/counters.h"
     10 #include "src/objects-inl.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 // -----------------------------------------------------------------------------
     16 // ES6 section 20.2.2 Function Properties of the Math Object
     17 
     18 class MathBuiltinsAssembler : public CodeStubAssembler {
     19  public:
     20   explicit MathBuiltinsAssembler(compiler::CodeAssemblerState* state)
     21       : CodeStubAssembler(state) {}
     22 
     23  protected:
     24   void MathRoundingOperation(Node* (CodeStubAssembler::*float64op)(Node*));
     25   void MathUnaryOperation(Node* (CodeStubAssembler::*float64op)(Node*));
     26 };
     27 
     28 // ES6 section - 20.2.2.1 Math.abs ( x )
     29 TF_BUILTIN(MathAbs, CodeStubAssembler) {
     30   Node* context = Parameter(4);
     31 
     32   // We might need to loop once for ToNumber conversion.
     33   Variable var_x(this, MachineRepresentation::kTagged);
     34   Label loop(this, &var_x);
     35   var_x.Bind(Parameter(1));
     36   Goto(&loop);
     37   Bind(&loop);
     38   {
     39     // Load the current {x} value.
     40     Node* x = var_x.value();
     41 
     42     // Check if {x} is a Smi or a HeapObject.
     43     Label if_xissmi(this), if_xisnotsmi(this);
     44     Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
     45 
     46     Bind(&if_xissmi);
     47     {
     48       // Check if {x} is already positive.
     49       Label if_xispositive(this), if_xisnotpositive(this);
     50       BranchIfSmiLessThanOrEqual(SmiConstant(Smi::FromInt(0)), x,
     51                                  &if_xispositive, &if_xisnotpositive);
     52 
     53       Bind(&if_xispositive);
     54       {
     55         // Just return the input {x}.
     56         Return(x);
     57       }
     58 
     59       Bind(&if_xisnotpositive);
     60       {
     61         // Try to negate the {x} value.
     62         Node* pair =
     63             IntPtrSubWithOverflow(IntPtrConstant(0), BitcastTaggedToWord(x));
     64         Node* overflow = Projection(1, pair);
     65         Label if_overflow(this, Label::kDeferred), if_notoverflow(this);
     66         Branch(overflow, &if_overflow, &if_notoverflow);
     67 
     68         Bind(&if_notoverflow);
     69         {
     70           // There is a Smi representation for negated {x}.
     71           Node* result = Projection(0, pair);
     72           Return(BitcastWordToTagged(result));
     73         }
     74 
     75         Bind(&if_overflow);
     76         { Return(NumberConstant(0.0 - Smi::kMinValue)); }
     77       }
     78     }
     79 
     80     Bind(&if_xisnotsmi);
     81     {
     82       // Check if {x} is a HeapNumber.
     83       Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
     84       Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
     85              &if_xisnotheapnumber);
     86 
     87       Bind(&if_xisheapnumber);
     88       {
     89         Node* x_value = LoadHeapNumberValue(x);
     90         Node* value = Float64Abs(x_value);
     91         Node* result = AllocateHeapNumberWithValue(value);
     92         Return(result);
     93       }
     94 
     95       Bind(&if_xisnotheapnumber);
     96       {
     97         // Need to convert {x} to a Number first.
     98         Callable callable = CodeFactory::NonNumberToNumber(isolate());
     99         var_x.Bind(CallStub(callable, context, x));
    100         Goto(&loop);
    101       }
    102     }
    103   }
    104 }
    105 
    106 void MathBuiltinsAssembler::MathRoundingOperation(
    107     Node* (CodeStubAssembler::*float64op)(Node*)) {
    108   Node* context = Parameter(4);
    109 
    110   // We might need to loop once for ToNumber conversion.
    111   Variable var_x(this, MachineRepresentation::kTagged);
    112   Label loop(this, &var_x);
    113   var_x.Bind(Parameter(1));
    114   Goto(&loop);
    115   Bind(&loop);
    116   {
    117     // Load the current {x} value.
    118     Node* x = var_x.value();
    119 
    120     // Check if {x} is a Smi or a HeapObject.
    121     Label if_xissmi(this), if_xisnotsmi(this);
    122     Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
    123 
    124     Bind(&if_xissmi);
    125     {
    126       // Nothing to do when {x} is a Smi.
    127       Return(x);
    128     }
    129 
    130     Bind(&if_xisnotsmi);
    131     {
    132       // Check if {x} is a HeapNumber.
    133       Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
    134       Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
    135              &if_xisnotheapnumber);
    136 
    137       Bind(&if_xisheapnumber);
    138       {
    139         Node* x_value = LoadHeapNumberValue(x);
    140         Node* value = (this->*float64op)(x_value);
    141         Node* result = ChangeFloat64ToTagged(value);
    142         Return(result);
    143       }
    144 
    145       Bind(&if_xisnotheapnumber);
    146       {
    147         // Need to convert {x} to a Number first.
    148         Callable callable = CodeFactory::NonNumberToNumber(isolate());
    149         var_x.Bind(CallStub(callable, context, x));
    150         Goto(&loop);
    151       }
    152     }
    153   }
    154 }
    155 
    156 void MathBuiltinsAssembler::MathUnaryOperation(
    157     Node* (CodeStubAssembler::*float64op)(Node*)) {
    158   Node* x = Parameter(1);
    159   Node* context = Parameter(4);
    160   Node* x_value = TruncateTaggedToFloat64(context, x);
    161   Node* value = (this->*float64op)(x_value);
    162   Node* result = AllocateHeapNumberWithValue(value);
    163   Return(result);
    164 }
    165 
    166 // ES6 section 20.2.2.2 Math.acos ( x )
    167 TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
    168   MathUnaryOperation(&CodeStubAssembler::Float64Acos);
    169 }
    170 
    171 // ES6 section 20.2.2.3 Math.acosh ( x )
    172 TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
    173   MathUnaryOperation(&CodeStubAssembler::Float64Acosh);
    174 }
    175 
    176 // ES6 section 20.2.2.4 Math.asin ( x )
    177 TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
    178   MathUnaryOperation(&CodeStubAssembler::Float64Asin);
    179 }
    180 
    181 // ES6 section 20.2.2.5 Math.asinh ( x )
    182 TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
    183   MathUnaryOperation(&CodeStubAssembler::Float64Asinh);
    184 }
    185 // ES6 section 20.2.2.6 Math.atan ( x )
    186 TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
    187   MathUnaryOperation(&CodeStubAssembler::Float64Atan);
    188 }
    189 
    190 // ES6 section 20.2.2.7 Math.atanh ( x )
    191 TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
    192   MathUnaryOperation(&CodeStubAssembler::Float64Atanh);
    193 }
    194 
    195 // ES6 section 20.2.2.8 Math.atan2 ( y, x )
    196 TF_BUILTIN(MathAtan2, CodeStubAssembler) {
    197   Node* y = Parameter(1);
    198   Node* x = Parameter(2);
    199   Node* context = Parameter(5);
    200 
    201   Node* y_value = TruncateTaggedToFloat64(context, y);
    202   Node* x_value = TruncateTaggedToFloat64(context, x);
    203   Node* value = Float64Atan2(y_value, x_value);
    204   Node* result = AllocateHeapNumberWithValue(value);
    205   Return(result);
    206 }
    207 
    208 // ES6 section 20.2.2.10 Math.ceil ( x )
    209 TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
    210   MathRoundingOperation(&CodeStubAssembler::Float64Ceil);
    211 }
    212 
    213 // ES6 section 20.2.2.9 Math.cbrt ( x )
    214 TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
    215   MathUnaryOperation(&CodeStubAssembler::Float64Cbrt);
    216 }
    217 
    218 // ES6 section 20.2.2.11 Math.clz32 ( x )
    219 TF_BUILTIN(MathClz32, CodeStubAssembler) {
    220   Node* context = Parameter(4);
    221 
    222   // Shared entry point for the clz32 operation.
    223   Variable var_clz32_x(this, MachineRepresentation::kWord32);
    224   Label do_clz32(this);
    225 
    226   // We might need to loop once for ToNumber conversion.
    227   Variable var_x(this, MachineRepresentation::kTagged);
    228   Label loop(this, &var_x);
    229   var_x.Bind(Parameter(1));
    230   Goto(&loop);
    231   Bind(&loop);
    232   {
    233     // Load the current {x} value.
    234     Node* x = var_x.value();
    235 
    236     // Check if {x} is a Smi or a HeapObject.
    237     Label if_xissmi(this), if_xisnotsmi(this);
    238     Branch(TaggedIsSmi(x), &if_xissmi, &if_xisnotsmi);
    239 
    240     Bind(&if_xissmi);
    241     {
    242       var_clz32_x.Bind(SmiToWord32(x));
    243       Goto(&do_clz32);
    244     }
    245 
    246     Bind(&if_xisnotsmi);
    247     {
    248       // Check if {x} is a HeapNumber.
    249       Label if_xisheapnumber(this), if_xisnotheapnumber(this, Label::kDeferred);
    250       Branch(IsHeapNumberMap(LoadMap(x)), &if_xisheapnumber,
    251              &if_xisnotheapnumber);
    252 
    253       Bind(&if_xisheapnumber);
    254       {
    255         var_clz32_x.Bind(TruncateHeapNumberValueToWord32(x));
    256         Goto(&do_clz32);
    257       }
    258 
    259       Bind(&if_xisnotheapnumber);
    260       {
    261         // Need to convert {x} to a Number first.
    262         Callable callable = CodeFactory::NonNumberToNumber(isolate());
    263         var_x.Bind(CallStub(callable, context, x));
    264         Goto(&loop);
    265       }
    266     }
    267   }
    268 
    269   Bind(&do_clz32);
    270   {
    271     Node* x_value = var_clz32_x.value();
    272     Node* value = Word32Clz(x_value);
    273     Node* result = ChangeInt32ToTagged(value);
    274     Return(result);
    275   }
    276 }
    277 
    278 // ES6 section 20.2.2.12 Math.cos ( x )
    279 TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
    280   MathUnaryOperation(&CodeStubAssembler::Float64Cos);
    281 }
    282 
    283 // ES6 section 20.2.2.13 Math.cosh ( x )
    284 TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
    285   MathUnaryOperation(&CodeStubAssembler::Float64Cosh);
    286 }
    287 
    288 // ES6 section 20.2.2.14 Math.exp ( x )
    289 TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
    290   MathUnaryOperation(&CodeStubAssembler::Float64Exp);
    291 }
    292 
    293 // ES6 section 20.2.2.15 Math.expm1 ( x )
    294 TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
    295   MathUnaryOperation(&CodeStubAssembler::Float64Expm1);
    296 }
    297 
    298 // ES6 section 20.2.2.16 Math.floor ( x )
    299 TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
    300   MathRoundingOperation(&CodeStubAssembler::Float64Floor);
    301 }
    302 
    303 // ES6 section 20.2.2.17 Math.fround ( x )
    304 TF_BUILTIN(MathFround, CodeStubAssembler) {
    305   Node* x = Parameter(1);
    306   Node* context = Parameter(4);
    307   Node* x_value = TruncateTaggedToFloat64(context, x);
    308   Node* value32 = TruncateFloat64ToFloat32(x_value);
    309   Node* value = ChangeFloat32ToFloat64(value32);
    310   Node* result = AllocateHeapNumberWithValue(value);
    311   Return(result);
    312 }
    313 
    314 // ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values )
    315 BUILTIN(MathHypot) {
    316   HandleScope scope(isolate);
    317   int const length = args.length() - 1;
    318   if (length == 0) return Smi::kZero;
    319   DCHECK_LT(0, length);
    320   double max = 0;
    321   bool one_arg_is_nan = false;
    322   List<double> abs_values(length);
    323   for (int i = 0; i < length; i++) {
    324     Handle<Object> x = args.at(i + 1);
    325     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x, Object::ToNumber(x));
    326     double abs_value = std::abs(x->Number());
    327 
    328     if (std::isnan(abs_value)) {
    329       one_arg_is_nan = true;
    330     } else {
    331       abs_values.Add(abs_value);
    332       if (max < abs_value) {
    333         max = abs_value;
    334       }
    335     }
    336   }
    337 
    338   if (max == V8_INFINITY) {
    339     return *isolate->factory()->NewNumber(V8_INFINITY);
    340   }
    341 
    342   if (one_arg_is_nan) {
    343     return isolate->heap()->nan_value();
    344   }
    345 
    346   if (max == 0) {
    347     return Smi::kZero;
    348   }
    349   DCHECK_GT(max, 0);
    350 
    351   // Kahan summation to avoid rounding errors.
    352   // Normalize the numbers to the largest one to avoid overflow.
    353   double sum = 0;
    354   double compensation = 0;
    355   for (int i = 0; i < length; i++) {
    356     double n = abs_values.at(i) / max;
    357     double summand = n * n - compensation;
    358     double preliminary = sum + summand;
    359     compensation = (preliminary - sum) - summand;
    360     sum = preliminary;
    361   }
    362 
    363   return *isolate->factory()->NewNumber(std::sqrt(sum) * max);
    364 }
    365 
    366 // ES6 section 20.2.2.19 Math.imul ( x, y )
    367 TF_BUILTIN(MathImul, CodeStubAssembler) {
    368   Node* x = Parameter(1);
    369   Node* y = Parameter(2);
    370   Node* context = Parameter(5);
    371   Node* x_value = TruncateTaggedToWord32(context, x);
    372   Node* y_value = TruncateTaggedToWord32(context, y);
    373   Node* value = Int32Mul(x_value, y_value);
    374   Node* result = ChangeInt32ToTagged(value);
    375   Return(result);
    376 }
    377 
    378 // ES6 section 20.2.2.20 Math.log ( x )
    379 TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
    380   MathUnaryOperation(&CodeStubAssembler::Float64Log);
    381 }
    382 
    383 // ES6 section 20.2.2.21 Math.log1p ( x )
    384 TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
    385   MathUnaryOperation(&CodeStubAssembler::Float64Log1p);
    386 }
    387 
    388 // ES6 section 20.2.2.22 Math.log10 ( x )
    389 TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
    390   MathUnaryOperation(&CodeStubAssembler::Float64Log10);
    391 }
    392 
    393 // ES6 section 20.2.2.23 Math.log2 ( x )
    394 TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
    395   MathUnaryOperation(&CodeStubAssembler::Float64Log2);
    396 }
    397 
    398 // ES6 section 20.2.2.26 Math.pow ( x, y )
    399 TF_BUILTIN(MathPow, CodeStubAssembler) {
    400   Node* x = Parameter(1);
    401   Node* y = Parameter(2);
    402   Node* context = Parameter(5);
    403   Node* x_value = TruncateTaggedToFloat64(context, x);
    404   Node* y_value = TruncateTaggedToFloat64(context, y);
    405   Node* value = Float64Pow(x_value, y_value);
    406   Node* result = ChangeFloat64ToTagged(value);
    407   Return(result);
    408 }
    409 
    410 // ES6 section 20.2.2.27 Math.random ( )
    411 TF_BUILTIN(MathRandom, CodeStubAssembler) {
    412   Node* context = Parameter(3);
    413   Node* native_context = LoadNativeContext(context);
    414 
    415   // Load cache index.
    416   Variable smi_index(this, MachineRepresentation::kTagged);
    417   smi_index.Bind(
    418       LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX));
    419 
    420   // Cached random numbers are exhausted if index is 0. Go to slow path.
    421   Label if_cached(this);
    422   GotoIf(SmiAbove(smi_index.value(), SmiConstant(Smi::kZero)), &if_cached);
    423 
    424   // Cache exhausted, populate the cache. Return value is the new index.
    425   smi_index.Bind(CallRuntime(Runtime::kGenerateRandomNumbers, context));
    426   Goto(&if_cached);
    427 
    428   // Compute next index by decrement.
    429   Bind(&if_cached);
    430   Node* new_smi_index = SmiSub(smi_index.value(), SmiConstant(Smi::FromInt(1)));
    431   StoreContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX,
    432                       new_smi_index);
    433 
    434   // Load and return next cached random number.
    435   Node* array =
    436       LoadContextElement(native_context, Context::MATH_RANDOM_CACHE_INDEX);
    437   Node* random = LoadFixedDoubleArrayElement(
    438       array, new_smi_index, MachineType::Float64(), 0, SMI_PARAMETERS);
    439   Return(AllocateHeapNumberWithValue(random));
    440 }
    441 
    442 // ES6 section 20.2.2.28 Math.round ( x )
    443 TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
    444   MathRoundingOperation(&CodeStubAssembler::Float64Round);
    445 }
    446 
    447 // ES6 section 20.2.2.29 Math.sign ( x )
    448 TF_BUILTIN(MathSign, CodeStubAssembler) {
    449   // Convert the {x} value to a Number.
    450   Node* x = Parameter(1);
    451   Node* context = Parameter(4);
    452   Node* x_value = TruncateTaggedToFloat64(context, x);
    453 
    454   // Return -1 if {x} is negative, 1 if {x} is positive, or {x} itself.
    455   Label if_xisnegative(this), if_xispositive(this);
    456   GotoIf(Float64LessThan(x_value, Float64Constant(0.0)), &if_xisnegative);
    457   GotoIf(Float64LessThan(Float64Constant(0.0), x_value), &if_xispositive);
    458   Return(ChangeFloat64ToTagged(x_value));
    459 
    460   Bind(&if_xisnegative);
    461   Return(SmiConstant(Smi::FromInt(-1)));
    462 
    463   Bind(&if_xispositive);
    464   Return(SmiConstant(Smi::FromInt(1)));
    465 }
    466 
    467 // ES6 section 20.2.2.30 Math.sin ( x )
    468 TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
    469   MathUnaryOperation(&CodeStubAssembler::Float64Sin);
    470 }
    471 
    472 // ES6 section 20.2.2.31 Math.sinh ( x )
    473 TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
    474   MathUnaryOperation(&CodeStubAssembler::Float64Sinh);
    475 }
    476 
    477 // ES6 section 20.2.2.32 Math.sqrt ( x )
    478 TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
    479   MathUnaryOperation(&CodeStubAssembler::Float64Sqrt);
    480 }
    481 
    482 // ES6 section 20.2.2.33 Math.tan ( x )
    483 TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
    484   MathUnaryOperation(&CodeStubAssembler::Float64Tan);
    485 }
    486 
    487 // ES6 section 20.2.2.34 Math.tanh ( x )
    488 TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
    489   MathUnaryOperation(&CodeStubAssembler::Float64Tanh);
    490 }
    491 
    492 // ES6 section 20.2.2.35 Math.trunc ( x )
    493 TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
    494   MathRoundingOperation(&CodeStubAssembler::Float64Trunc);
    495 }
    496 
    497 void Builtins::Generate_MathMax(MacroAssembler* masm) {
    498   Generate_MathMaxMin(masm, MathMaxMinKind::kMax);
    499 }
    500 
    501 void Builtins::Generate_MathMin(MacroAssembler* masm) {
    502   Generate_MathMaxMin(masm, MathMaxMinKind::kMin);
    503 }
    504 
    505 }  // namespace internal
    506 }  // namespace v8
    507