• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
MathBuiltinsAssembler(compiler::CodeAssemblerState * state)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 )
TF_BUILTIN(MathAbs,CodeStubAssembler)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 
MathRoundingOperation(Node * (CodeStubAssembler::* float64op)(Node *))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 
MathUnaryOperation(Node * (CodeStubAssembler::* float64op)(Node *))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 )
TF_BUILTIN(MathAcos,MathBuiltinsAssembler)167 TF_BUILTIN(MathAcos, MathBuiltinsAssembler) {
168   MathUnaryOperation(&CodeStubAssembler::Float64Acos);
169 }
170 
171 // ES6 section 20.2.2.3 Math.acosh ( x )
TF_BUILTIN(MathAcosh,MathBuiltinsAssembler)172 TF_BUILTIN(MathAcosh, MathBuiltinsAssembler) {
173   MathUnaryOperation(&CodeStubAssembler::Float64Acosh);
174 }
175 
176 // ES6 section 20.2.2.4 Math.asin ( x )
TF_BUILTIN(MathAsin,MathBuiltinsAssembler)177 TF_BUILTIN(MathAsin, MathBuiltinsAssembler) {
178   MathUnaryOperation(&CodeStubAssembler::Float64Asin);
179 }
180 
181 // ES6 section 20.2.2.5 Math.asinh ( x )
TF_BUILTIN(MathAsinh,MathBuiltinsAssembler)182 TF_BUILTIN(MathAsinh, MathBuiltinsAssembler) {
183   MathUnaryOperation(&CodeStubAssembler::Float64Asinh);
184 }
185 // ES6 section 20.2.2.6 Math.atan ( x )
TF_BUILTIN(MathAtan,MathBuiltinsAssembler)186 TF_BUILTIN(MathAtan, MathBuiltinsAssembler) {
187   MathUnaryOperation(&CodeStubAssembler::Float64Atan);
188 }
189 
190 // ES6 section 20.2.2.7 Math.atanh ( x )
TF_BUILTIN(MathAtanh,MathBuiltinsAssembler)191 TF_BUILTIN(MathAtanh, MathBuiltinsAssembler) {
192   MathUnaryOperation(&CodeStubAssembler::Float64Atanh);
193 }
194 
195 // ES6 section 20.2.2.8 Math.atan2 ( y, x )
TF_BUILTIN(MathAtan2,CodeStubAssembler)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 )
TF_BUILTIN(MathCeil,MathBuiltinsAssembler)209 TF_BUILTIN(MathCeil, MathBuiltinsAssembler) {
210   MathRoundingOperation(&CodeStubAssembler::Float64Ceil);
211 }
212 
213 // ES6 section 20.2.2.9 Math.cbrt ( x )
TF_BUILTIN(MathCbrt,MathBuiltinsAssembler)214 TF_BUILTIN(MathCbrt, MathBuiltinsAssembler) {
215   MathUnaryOperation(&CodeStubAssembler::Float64Cbrt);
216 }
217 
218 // ES6 section 20.2.2.11 Math.clz32 ( x )
TF_BUILTIN(MathClz32,CodeStubAssembler)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 )
TF_BUILTIN(MathCos,MathBuiltinsAssembler)279 TF_BUILTIN(MathCos, MathBuiltinsAssembler) {
280   MathUnaryOperation(&CodeStubAssembler::Float64Cos);
281 }
282 
283 // ES6 section 20.2.2.13 Math.cosh ( x )
TF_BUILTIN(MathCosh,MathBuiltinsAssembler)284 TF_BUILTIN(MathCosh, MathBuiltinsAssembler) {
285   MathUnaryOperation(&CodeStubAssembler::Float64Cosh);
286 }
287 
288 // ES6 section 20.2.2.14 Math.exp ( x )
TF_BUILTIN(MathExp,MathBuiltinsAssembler)289 TF_BUILTIN(MathExp, MathBuiltinsAssembler) {
290   MathUnaryOperation(&CodeStubAssembler::Float64Exp);
291 }
292 
293 // ES6 section 20.2.2.15 Math.expm1 ( x )
TF_BUILTIN(MathExpm1,MathBuiltinsAssembler)294 TF_BUILTIN(MathExpm1, MathBuiltinsAssembler) {
295   MathUnaryOperation(&CodeStubAssembler::Float64Expm1);
296 }
297 
298 // ES6 section 20.2.2.16 Math.floor ( x )
TF_BUILTIN(MathFloor,MathBuiltinsAssembler)299 TF_BUILTIN(MathFloor, MathBuiltinsAssembler) {
300   MathRoundingOperation(&CodeStubAssembler::Float64Floor);
301 }
302 
303 // ES6 section 20.2.2.17 Math.fround ( x )
TF_BUILTIN(MathFround,CodeStubAssembler)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 )
BUILTIN(MathHypot)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 )
TF_BUILTIN(MathImul,CodeStubAssembler)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 )
TF_BUILTIN(MathLog,MathBuiltinsAssembler)379 TF_BUILTIN(MathLog, MathBuiltinsAssembler) {
380   MathUnaryOperation(&CodeStubAssembler::Float64Log);
381 }
382 
383 // ES6 section 20.2.2.21 Math.log1p ( x )
TF_BUILTIN(MathLog1p,MathBuiltinsAssembler)384 TF_BUILTIN(MathLog1p, MathBuiltinsAssembler) {
385   MathUnaryOperation(&CodeStubAssembler::Float64Log1p);
386 }
387 
388 // ES6 section 20.2.2.22 Math.log10 ( x )
TF_BUILTIN(MathLog10,MathBuiltinsAssembler)389 TF_BUILTIN(MathLog10, MathBuiltinsAssembler) {
390   MathUnaryOperation(&CodeStubAssembler::Float64Log10);
391 }
392 
393 // ES6 section 20.2.2.23 Math.log2 ( x )
TF_BUILTIN(MathLog2,MathBuiltinsAssembler)394 TF_BUILTIN(MathLog2, MathBuiltinsAssembler) {
395   MathUnaryOperation(&CodeStubAssembler::Float64Log2);
396 }
397 
398 // ES6 section 20.2.2.26 Math.pow ( x, y )
TF_BUILTIN(MathPow,CodeStubAssembler)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 ( )
TF_BUILTIN(MathRandom,CodeStubAssembler)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 )
TF_BUILTIN(MathRound,MathBuiltinsAssembler)443 TF_BUILTIN(MathRound, MathBuiltinsAssembler) {
444   MathRoundingOperation(&CodeStubAssembler::Float64Round);
445 }
446 
447 // ES6 section 20.2.2.29 Math.sign ( x )
TF_BUILTIN(MathSign,CodeStubAssembler)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 )
TF_BUILTIN(MathSin,MathBuiltinsAssembler)468 TF_BUILTIN(MathSin, MathBuiltinsAssembler) {
469   MathUnaryOperation(&CodeStubAssembler::Float64Sin);
470 }
471 
472 // ES6 section 20.2.2.31 Math.sinh ( x )
TF_BUILTIN(MathSinh,MathBuiltinsAssembler)473 TF_BUILTIN(MathSinh, MathBuiltinsAssembler) {
474   MathUnaryOperation(&CodeStubAssembler::Float64Sinh);
475 }
476 
477 // ES6 section 20.2.2.32 Math.sqrt ( x )
TF_BUILTIN(MathSqrt,MathBuiltinsAssembler)478 TF_BUILTIN(MathSqrt, MathBuiltinsAssembler) {
479   MathUnaryOperation(&CodeStubAssembler::Float64Sqrt);
480 }
481 
482 // ES6 section 20.2.2.33 Math.tan ( x )
TF_BUILTIN(MathTan,MathBuiltinsAssembler)483 TF_BUILTIN(MathTan, MathBuiltinsAssembler) {
484   MathUnaryOperation(&CodeStubAssembler::Float64Tan);
485 }
486 
487 // ES6 section 20.2.2.34 Math.tanh ( x )
TF_BUILTIN(MathTanh,MathBuiltinsAssembler)488 TF_BUILTIN(MathTanh, MathBuiltinsAssembler) {
489   MathUnaryOperation(&CodeStubAssembler::Float64Tanh);
490 }
491 
492 // ES6 section 20.2.2.35 Math.trunc ( x )
TF_BUILTIN(MathTrunc,MathBuiltinsAssembler)493 TF_BUILTIN(MathTrunc, MathBuiltinsAssembler) {
494   MathRoundingOperation(&CodeStubAssembler::Float64Trunc);
495 }
496 
Generate_MathMax(MacroAssembler * masm)497 void Builtins::Generate_MathMax(MacroAssembler* masm) {
498   Generate_MathMaxMin(masm, MathMaxMinKind::kMax);
499 }
500 
Generate_MathMin(MacroAssembler * masm)501 void Builtins::Generate_MathMin(MacroAssembler* masm) {
502   Generate_MathMaxMin(masm, MathMaxMinKind::kMin);
503 }
504 
505 }  // namespace internal
506 }  // namespace v8
507