• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/builtins/builtins-utils-gen.h"
7 #include "src/builtins/builtins.h"
8 #include "src/code-stub-assembler.h"
9 #include "src/ic/binary-op-assembler.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 // -----------------------------------------------------------------------------
15 // ES6 section 20.1 Number Objects
16 
17 class NumberBuiltinsAssembler : public CodeStubAssembler {
18  public:
NumberBuiltinsAssembler(compiler::CodeAssemblerState * state)19   explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state)
20       : CodeStubAssembler(state) {}
21 
22  protected:
23   template <typename Descriptor>
EmitBitwiseOp(Operation op)24   void EmitBitwiseOp(Operation op) {
25     Node* left = Parameter(Descriptor::kLeft);
26     Node* right = Parameter(Descriptor::kRight);
27     Node* context = Parameter(Descriptor::kContext);
28 
29     VARIABLE(var_left_word32, MachineRepresentation::kWord32);
30     VARIABLE(var_right_word32, MachineRepresentation::kWord32);
31     VARIABLE(var_left_bigint, MachineRepresentation::kTagged, left);
32     VARIABLE(var_right_bigint, MachineRepresentation::kTagged);
33     Label if_left_number(this), do_number_op(this);
34     Label if_left_bigint(this), do_bigint_op(this);
35 
36     TaggedToWord32OrBigInt(context, left, &if_left_number, &var_left_word32,
37                            &if_left_bigint, &var_left_bigint);
38     BIND(&if_left_number);
39     TaggedToWord32OrBigInt(context, right, &do_number_op, &var_right_word32,
40                            &do_bigint_op, &var_right_bigint);
41     BIND(&do_number_op);
42     Return(BitwiseOp(var_left_word32.value(), var_right_word32.value(), op));
43 
44     // BigInt cases.
45     BIND(&if_left_bigint);
46     TaggedToNumeric(context, right, &do_bigint_op, &var_right_bigint);
47 
48     BIND(&do_bigint_op);
49     Return(CallRuntime(Runtime::kBigIntBinaryOp, context,
50                        var_left_bigint.value(), var_right_bigint.value(),
51                        SmiConstant(op)));
52   }
53 
54   template <typename Descriptor>
RelationalComparisonBuiltin(Operation op)55   void RelationalComparisonBuiltin(Operation op) {
56     Node* lhs = Parameter(Descriptor::kLeft);
57     Node* rhs = Parameter(Descriptor::kRight);
58     Node* context = Parameter(Descriptor::kContext);
59 
60     Return(RelationalComparison(op, lhs, rhs, context));
61   }
62 
63   template <typename Descriptor>
64   void UnaryOp(Variable* var_input, Label* do_smi, Label* do_double,
65                Variable* var_input_double, Label* do_bigint);
66 
67   template <typename Descriptor>
68   void BinaryOp(Label* smis, Variable* var_left, Variable* var_right,
69                 Label* doubles, Variable* var_left_double,
70                 Variable* var_right_double, Label* bigints);
71 };
72 
73 // ES6 #sec-number.isfinite
TF_BUILTIN(NumberIsFinite,CodeStubAssembler)74 TF_BUILTIN(NumberIsFinite, CodeStubAssembler) {
75   Node* number = Parameter(Descriptor::kNumber);
76 
77   Label return_true(this), return_false(this);
78 
79   // Check if {number} is a Smi.
80   GotoIf(TaggedIsSmi(number), &return_true);
81 
82   // Check if {number} is a HeapNumber.
83   GotoIfNot(IsHeapNumber(number), &return_false);
84 
85   // Check if {number} contains a finite, non-NaN value.
86   Node* number_value = LoadHeapNumberValue(number);
87   BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false,
88                        &return_true);
89 
90   BIND(&return_true);
91   Return(TrueConstant());
92 
93   BIND(&return_false);
94   Return(FalseConstant());
95 }
96 
TF_BUILTIN(AllocateHeapNumber,CodeStubAssembler)97 TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) {
98   Node* result = AllocateHeapNumber();
99   Return(result);
100 }
101 
102 // ES6 #sec-number.isinteger
TF_BUILTIN(NumberIsInteger,CodeStubAssembler)103 TF_BUILTIN(NumberIsInteger, CodeStubAssembler) {
104   TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
105   Return(SelectBooleanConstant(IsInteger(number)));
106 }
107 
108 // ES6 #sec-number.isnan
TF_BUILTIN(NumberIsNaN,CodeStubAssembler)109 TF_BUILTIN(NumberIsNaN, CodeStubAssembler) {
110   Node* number = Parameter(Descriptor::kNumber);
111 
112   Label return_true(this), return_false(this);
113 
114   // Check if {number} is a Smi.
115   GotoIf(TaggedIsSmi(number), &return_false);
116 
117   // Check if {number} is a HeapNumber.
118   GotoIfNot(IsHeapNumber(number), &return_false);
119 
120   // Check if {number} contains a NaN value.
121   Node* number_value = LoadHeapNumberValue(number);
122   BranchIfFloat64IsNaN(number_value, &return_true, &return_false);
123 
124   BIND(&return_true);
125   Return(TrueConstant());
126 
127   BIND(&return_false);
128   Return(FalseConstant());
129 }
130 
131 // ES6 #sec-number.issafeinteger
TF_BUILTIN(NumberIsSafeInteger,CodeStubAssembler)132 TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) {
133   TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
134   Return(SelectBooleanConstant(IsSafeInteger(number)));
135 }
136 
137 // ES6 #sec-number.parsefloat
TF_BUILTIN(NumberParseFloat,CodeStubAssembler)138 TF_BUILTIN(NumberParseFloat, CodeStubAssembler) {
139   Node* context = Parameter(Descriptor::kContext);
140 
141   // We might need to loop once for ToString conversion.
142   VARIABLE(var_input, MachineRepresentation::kTagged,
143            Parameter(Descriptor::kString));
144   Label loop(this, &var_input);
145   Goto(&loop);
146   BIND(&loop);
147   {
148     // Load the current {input} value.
149     Node* input = var_input.value();
150 
151     // Check if the {input} is a HeapObject or a Smi.
152     Label if_inputissmi(this), if_inputisnotsmi(this);
153     Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi);
154 
155     BIND(&if_inputissmi);
156     {
157       // The {input} is already a Number, no need to do anything.
158       Return(input);
159     }
160 
161     BIND(&if_inputisnotsmi);
162     {
163       // The {input} is a HeapObject, check if it's already a String.
164       Label if_inputisstring(this), if_inputisnotstring(this);
165       Node* input_map = LoadMap(input);
166       Node* input_instance_type = LoadMapInstanceType(input_map);
167       Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
168              &if_inputisnotstring);
169 
170       BIND(&if_inputisstring);
171       {
172         // The {input} is already a String, check if {input} contains
173         // a cached array index.
174         Label if_inputcached(this), if_inputnotcached(this);
175         Node* input_hash = LoadNameHashField(input);
176         Branch(IsClearWord32(input_hash,
177                              Name::kDoesNotContainCachedArrayIndexMask),
178                &if_inputcached, &if_inputnotcached);
179 
180         BIND(&if_inputcached);
181         {
182           // Just return the {input}s cached array index.
183           Node* input_array_index =
184               DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
185           Return(SmiTag(input_array_index));
186         }
187 
188         BIND(&if_inputnotcached);
189         {
190           // Need to fall back to the runtime to convert {input} to double.
191           Return(CallRuntime(Runtime::kStringParseFloat, context, input));
192         }
193       }
194 
195       BIND(&if_inputisnotstring);
196       {
197         // The {input} is neither a String nor a Smi, check for HeapNumber.
198         Label if_inputisnumber(this),
199             if_inputisnotnumber(this, Label::kDeferred);
200         Branch(IsHeapNumberMap(input_map), &if_inputisnumber,
201                &if_inputisnotnumber);
202 
203         BIND(&if_inputisnumber);
204         {
205           // The {input} is already a Number, take care of -0.
206           Label if_inputiszero(this), if_inputisnotzero(this);
207           Node* input_value = LoadHeapNumberValue(input);
208           Branch(Float64Equal(input_value, Float64Constant(0.0)),
209                  &if_inputiszero, &if_inputisnotzero);
210 
211           BIND(&if_inputiszero);
212           Return(SmiConstant(0));
213 
214           BIND(&if_inputisnotzero);
215           Return(input);
216         }
217 
218         BIND(&if_inputisnotnumber);
219         {
220           // Need to convert the {input} to String first.
221           // TODO(bmeurer): This could be more efficient if necessary.
222           var_input.Bind(CallBuiltin(Builtins::kToString, context, input));
223           Goto(&loop);
224         }
225       }
226     }
227   }
228 }
229 
230 // ES6 #sec-number.parseint
TF_BUILTIN(ParseInt,CodeStubAssembler)231 TF_BUILTIN(ParseInt, CodeStubAssembler) {
232   Node* context = Parameter(Descriptor::kContext);
233   Node* input = Parameter(Descriptor::kString);
234   Node* radix = Parameter(Descriptor::kRadix);
235 
236   // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10).
237   Label if_radix10(this), if_generic(this, Label::kDeferred);
238   GotoIf(IsUndefined(radix), &if_radix10);
239   GotoIf(WordEqual(radix, SmiConstant(10)), &if_radix10);
240   GotoIf(WordEqual(radix, SmiConstant(0)), &if_radix10);
241   Goto(&if_generic);
242 
243   BIND(&if_radix10);
244   {
245     // Check if we can avoid the ToString conversion on {input}.
246     Label if_inputissmi(this), if_inputisheapnumber(this),
247         if_inputisstring(this);
248     GotoIf(TaggedIsSmi(input), &if_inputissmi);
249     Node* input_map = LoadMap(input);
250     GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber);
251     Node* input_instance_type = LoadMapInstanceType(input_map);
252     Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
253            &if_generic);
254 
255     BIND(&if_inputissmi);
256     {
257       // Just return the {input}.
258       Return(input);
259     }
260 
261     BIND(&if_inputisheapnumber);
262     {
263       // Check if the {input} value is in Signed32 range.
264       Label if_inputissigned32(this);
265       Node* input_value = LoadHeapNumberValue(input);
266       Node* input_value32 = TruncateFloat64ToWord32(input_value);
267       GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)),
268              &if_inputissigned32);
269 
270       // Check if the absolute {input} value is in the [1,1<<31[ range.
271       // Take the generic path for the range [0,1[ because the result
272       // could be -0.
273       Node* input_value_abs = Float64Abs(input_value);
274 
275       GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1u << 31)),
276                 &if_generic);
277       Branch(Float64LessThanOrEqual(Float64Constant(1), input_value_abs),
278              &if_inputissigned32, &if_generic);
279 
280       // Return the truncated int32 value, and return the tagged result.
281       BIND(&if_inputissigned32);
282       Node* result = ChangeInt32ToTagged(input_value32);
283       Return(result);
284     }
285 
286     BIND(&if_inputisstring);
287     {
288       // Check if the String {input} has a cached array index.
289       Node* input_hash = LoadNameHashField(input);
290       GotoIf(IsSetWord32(input_hash, Name::kDoesNotContainCachedArrayIndexMask),
291              &if_generic);
292 
293       // Return the cached array index as result.
294       Node* input_index =
295           DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
296       Node* result = SmiTag(input_index);
297       Return(result);
298     }
299   }
300 
301   BIND(&if_generic);
302   {
303     Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix);
304     Return(result);
305   }
306 }
307 
308 // ES6 #sec-number.parseint
TF_BUILTIN(NumberParseInt,CodeStubAssembler)309 TF_BUILTIN(NumberParseInt, CodeStubAssembler) {
310   Node* context = Parameter(Descriptor::kContext);
311   Node* input = Parameter(Descriptor::kString);
312   Node* radix = Parameter(Descriptor::kRadix);
313   Return(CallBuiltin(Builtins::kParseInt, context, input, radix));
314 }
315 
316 // ES6 #sec-number.prototype.valueof
TF_BUILTIN(NumberPrototypeValueOf,CodeStubAssembler)317 TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) {
318   Node* context = Parameter(Descriptor::kContext);
319   Node* receiver = Parameter(Descriptor::kReceiver);
320 
321   Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber,
322                              "Number.prototype.valueOf");
323   Return(result);
324 }
325 
326 class AddStubAssembler : public CodeStubAssembler {
327  public:
AddStubAssembler(compiler::CodeAssemblerState * state)328   explicit AddStubAssembler(compiler::CodeAssemblerState* state)
329       : CodeStubAssembler(state) {}
330 
331  protected:
ConvertReceiverAndLoop(Variable * var_value,Label * loop,Node * context)332   void ConvertReceiverAndLoop(Variable* var_value, Label* loop, Node* context) {
333     // Call ToPrimitive explicitly without hint (whereas ToNumber
334     // would pass a "number" hint).
335     Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate());
336     var_value->Bind(CallStub(callable, context, var_value->value()));
337     Goto(loop);
338   }
339 
ConvertNonReceiverAndLoop(Variable * var_value,Label * loop,Node * context)340   void ConvertNonReceiverAndLoop(Variable* var_value, Label* loop,
341                                  Node* context) {
342     var_value->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
343                                 var_value->value()));
344     Goto(loop);
345   }
346 
ConvertAndLoop(Variable * var_value,Node * instance_type,Label * loop,Node * context)347   void ConvertAndLoop(Variable* var_value, Node* instance_type, Label* loop,
348                       Node* context) {
349     Label is_not_receiver(this, Label::kDeferred);
350     GotoIfNot(IsJSReceiverInstanceType(instance_type), &is_not_receiver);
351 
352     ConvertReceiverAndLoop(var_value, loop, context);
353 
354     BIND(&is_not_receiver);
355     ConvertNonReceiverAndLoop(var_value, loop, context);
356   }
357 };
358 
TF_BUILTIN(Add,AddStubAssembler)359 TF_BUILTIN(Add, AddStubAssembler) {
360   Node* context = Parameter(Descriptor::kContext);
361   VARIABLE(var_left, MachineRepresentation::kTagged,
362            Parameter(Descriptor::kLeft));
363   VARIABLE(var_right, MachineRepresentation::kTagged,
364            Parameter(Descriptor::kRight));
365 
366   // Shared entry for floating point addition.
367   Label do_double_add(this);
368   VARIABLE(var_left_double, MachineRepresentation::kFloat64);
369   VARIABLE(var_right_double, MachineRepresentation::kFloat64);
370 
371   // We might need to loop several times due to ToPrimitive, ToString and/or
372   // ToNumeric conversions.
373   VARIABLE(var_result, MachineRepresentation::kTagged);
374   Variable* loop_vars[2] = {&var_left, &var_right};
375   Label loop(this, 2, loop_vars),
376       string_add_convert_left(this, Label::kDeferred),
377       string_add_convert_right(this, Label::kDeferred),
378       do_bigint_add(this, Label::kDeferred);
379   Goto(&loop);
380   BIND(&loop);
381   {
382     Node* left = var_left.value();
383     Node* right = var_right.value();
384 
385     Label if_left_smi(this), if_left_heapobject(this);
386     Branch(TaggedIsSmi(left), &if_left_smi, &if_left_heapobject);
387 
388     BIND(&if_left_smi);
389     {
390       Label if_right_smi(this), if_right_heapobject(this);
391       Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject);
392 
393       BIND(&if_right_smi);
394       {
395         Label if_overflow(this);
396         TNode<Smi> result = TrySmiAdd(CAST(left), CAST(right), &if_overflow);
397         Return(result);
398 
399         BIND(&if_overflow);
400         {
401           var_left_double.Bind(SmiToFloat64(left));
402           var_right_double.Bind(SmiToFloat64(right));
403           Goto(&do_double_add);
404         }
405       }  // if_right_smi
406 
407       BIND(&if_right_heapobject);
408       {
409         Node* right_map = LoadMap(right);
410 
411         Label if_right_not_number(this, Label::kDeferred);
412         GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number);
413 
414         // {right} is a HeapNumber.
415         var_left_double.Bind(SmiToFloat64(left));
416         var_right_double.Bind(LoadHeapNumberValue(right));
417         Goto(&do_double_add);
418 
419         BIND(&if_right_not_number);
420         {
421           Node* right_instance_type = LoadMapInstanceType(right_map);
422           GotoIf(IsStringInstanceType(right_instance_type),
423                  &string_add_convert_left);
424           GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
425           ConvertAndLoop(&var_right, right_instance_type, &loop, context);
426         }
427       }  // if_right_heapobject
428     }    // if_left_smi
429 
430     BIND(&if_left_heapobject);
431     {
432       Node* left_map = LoadMap(left);
433       Label if_right_smi(this), if_right_heapobject(this);
434       Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject);
435 
436       BIND(&if_right_smi);
437       {
438         Label if_left_not_number(this, Label::kDeferred);
439         GotoIfNot(IsHeapNumberMap(left_map), &if_left_not_number);
440 
441         // {left} is a HeapNumber, {right} is a Smi.
442         var_left_double.Bind(LoadHeapNumberValue(left));
443         var_right_double.Bind(SmiToFloat64(right));
444         Goto(&do_double_add);
445 
446         BIND(&if_left_not_number);
447         {
448           Node* left_instance_type = LoadMapInstanceType(left_map);
449           GotoIf(IsStringInstanceType(left_instance_type),
450                  &string_add_convert_right);
451           GotoIf(IsBigIntInstanceType(left_instance_type), &do_bigint_add);
452           // {left} is neither a Numeric nor a String, and {right} is a Smi.
453           ConvertAndLoop(&var_left, left_instance_type, &loop, context);
454         }
455       }  // if_right_smi
456 
457       BIND(&if_right_heapobject);
458       {
459         Node* right_map = LoadMap(right);
460 
461         Label if_left_number(this), if_left_not_number(this, Label::kDeferred);
462         Branch(IsHeapNumberMap(left_map), &if_left_number, &if_left_not_number);
463 
464         BIND(&if_left_number);
465         {
466           Label if_right_not_number(this, Label::kDeferred);
467           GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number);
468 
469           // Both {left} and {right} are HeapNumbers.
470           var_left_double.Bind(LoadHeapNumberValue(left));
471           var_right_double.Bind(LoadHeapNumberValue(right));
472           Goto(&do_double_add);
473 
474           BIND(&if_right_not_number);
475           {
476             Node* right_instance_type = LoadMapInstanceType(right_map);
477             GotoIf(IsStringInstanceType(right_instance_type),
478                    &string_add_convert_left);
479             GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
480             // {left} is a HeapNumber, {right} is neither Number nor String.
481             ConvertAndLoop(&var_right, right_instance_type, &loop, context);
482           }
483         }  // if_left_number
484 
485         BIND(&if_left_not_number);
486         {
487           Label if_left_bigint(this);
488           Node* left_instance_type = LoadMapInstanceType(left_map);
489           GotoIf(IsStringInstanceType(left_instance_type),
490                  &string_add_convert_right);
491           Node* right_instance_type = LoadMapInstanceType(right_map);
492           GotoIf(IsStringInstanceType(right_instance_type),
493                  &string_add_convert_left);
494           GotoIf(IsBigIntInstanceType(left_instance_type), &if_left_bigint);
495           Label if_left_not_receiver(this, Label::kDeferred);
496           Label if_right_not_receiver(this, Label::kDeferred);
497           GotoIfNot(IsJSReceiverInstanceType(left_instance_type),
498                     &if_left_not_receiver);
499           // {left} is a JSReceiver, convert it first.
500           ConvertReceiverAndLoop(&var_left, &loop, context);
501 
502           BIND(&if_left_bigint);
503           {
504             // {right} is a HeapObject, but not a String. Jump to
505             // {do_bigint_add} if {right} is already a Numeric.
506             GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add);
507             GotoIf(IsHeapNumberMap(right_map), &do_bigint_add);
508             ConvertAndLoop(&var_right, right_instance_type, &loop, context);
509           }
510 
511           BIND(&if_left_not_receiver);
512           GotoIfNot(IsJSReceiverInstanceType(right_instance_type),
513                     &if_right_not_receiver);
514           // {left} is a Primitive, but {right} is a JSReceiver, so convert
515           // {right} with priority.
516           ConvertReceiverAndLoop(&var_right, &loop, context);
517 
518           BIND(&if_right_not_receiver);
519           // Neither {left} nor {right} are JSReceivers.
520           ConvertNonReceiverAndLoop(&var_left, &loop, context);
521         }
522       }  // if_right_heapobject
523     }    // if_left_heapobject
524   }
525   BIND(&string_add_convert_left);
526   {
527     // Convert {left} to a String and concatenate it with the String {right}.
528     Callable callable =
529         CodeFactory::StringAdd(isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED);
530     Return(CallStub(callable, context, var_left.value(), var_right.value()));
531   }
532 
533   BIND(&string_add_convert_right);
534   {
535     // Convert {right} to a String and concatenate it with the String {left}.
536     Callable callable = CodeFactory::StringAdd(
537         isolate(), STRING_ADD_CONVERT_RIGHT, NOT_TENURED);
538     Return(CallStub(callable, context, var_left.value(), var_right.value()));
539   }
540 
541   BIND(&do_bigint_add);
542   {
543     Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
544                        var_right.value(), SmiConstant(Operation::kAdd)));
545   }
546 
547   BIND(&do_double_add);
548   {
549     Node* value = Float64Add(var_left_double.value(), var_right_double.value());
550     Return(AllocateHeapNumberWithValue(value));
551   }
552 }
553 
554 template <typename Descriptor>
UnaryOp(Variable * var_input,Label * do_smi,Label * do_double,Variable * var_input_double,Label * do_bigint)555 void NumberBuiltinsAssembler::UnaryOp(Variable* var_input, Label* do_smi,
556                                       Label* do_double,
557                                       Variable* var_input_double,
558                                       Label* do_bigint) {
559   DCHECK_EQ(var_input->rep(), MachineRepresentation::kTagged);
560   DCHECK_IMPLIES(var_input_double != nullptr,
561                  var_input_double->rep() == MachineRepresentation::kFloat64);
562 
563   Node* context = Parameter(Descriptor::kContext);
564   var_input->Bind(Parameter(Descriptor::kValue));
565 
566   // We might need to loop for ToNumeric conversion.
567   Label loop(this, {var_input});
568   Goto(&loop);
569   BIND(&loop);
570   Node* input = var_input->value();
571 
572   Label not_number(this);
573   GotoIf(TaggedIsSmi(input), do_smi);
574   GotoIfNot(IsHeapNumber(input), &not_number);
575   if (var_input_double != nullptr) {
576     var_input_double->Bind(LoadHeapNumberValue(input));
577   }
578   Goto(do_double);
579 
580   BIND(&not_number);
581   GotoIf(IsBigInt(input), do_bigint);
582   var_input->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, input));
583   Goto(&loop);
584 }
585 
586 template <typename Descriptor>
BinaryOp(Label * smis,Variable * var_left,Variable * var_right,Label * doubles,Variable * var_left_double,Variable * var_right_double,Label * bigints)587 void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left,
588                                        Variable* var_right, Label* doubles,
589                                        Variable* var_left_double,
590                                        Variable* var_right_double,
591                                        Label* bigints) {
592   DCHECK_EQ(var_left->rep(), MachineRepresentation::kTagged);
593   DCHECK_EQ(var_right->rep(), MachineRepresentation::kTagged);
594   DCHECK_IMPLIES(var_left_double != nullptr,
595                  var_left_double->rep() == MachineRepresentation::kFloat64);
596   DCHECK_IMPLIES(var_right_double != nullptr,
597                  var_right_double->rep() == MachineRepresentation::kFloat64);
598   DCHECK_EQ(var_left_double == nullptr, var_right_double == nullptr);
599 
600   Node* context = Parameter(Descriptor::kContext);
601   var_left->Bind(Parameter(Descriptor::kLeft));
602   var_right->Bind(Parameter(Descriptor::kRight));
603 
604   // We might need to loop for ToNumeric conversions.
605   Label loop(this, {var_left, var_right});
606   Goto(&loop);
607   BIND(&loop);
608 
609   Label left_not_smi(this), right_not_smi(this);
610   Label left_not_number(this), right_not_number(this);
611   GotoIfNot(TaggedIsSmi(var_left->value()), &left_not_smi);
612   GotoIf(TaggedIsSmi(var_right->value()), smis);
613 
614   // At this point, var_left is a Smi but var_right is not.
615   GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
616   if (var_left_double != nullptr) {
617     var_left_double->Bind(SmiToFloat64(var_left->value()));
618     var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
619   }
620   Goto(doubles);
621 
622   BIND(&left_not_smi);
623   {
624     GotoIfNot(IsHeapNumber(var_left->value()), &left_not_number);
625     GotoIfNot(TaggedIsSmi(var_right->value()), &right_not_smi);
626 
627     // At this point, var_left is a HeapNumber and var_right is a Smi.
628     if (var_left_double != nullptr) {
629       var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
630       var_right_double->Bind(SmiToFloat64(var_right->value()));
631     }
632     Goto(doubles);
633   }
634 
635   BIND(&right_not_smi);
636   {
637     GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
638     if (var_left_double != nullptr) {
639       var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
640       var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
641     }
642     Goto(doubles);
643   }
644 
645   BIND(&left_not_number);
646   {
647     Label left_bigint(this);
648     GotoIf(IsBigInt(var_left->value()), &left_bigint);
649     var_left->Bind(
650         CallBuiltin(Builtins::kNonNumberToNumeric, context, var_left->value()));
651     Goto(&loop);
652 
653     BIND(&left_bigint);
654     {
655       // Jump to {bigints} if {var_right} is already a Numeric.
656       GotoIf(TaggedIsSmi(var_right->value()), bigints);
657       GotoIf(IsBigInt(var_right->value()), bigints);
658       GotoIf(IsHeapNumber(var_right->value()), bigints);
659       var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
660                                   var_right->value()));
661       Goto(&loop);
662     }
663   }
664 
665   BIND(&right_not_number);
666   {
667     GotoIf(IsBigInt(var_right->value()), bigints);
668     var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
669                                 var_right->value()));
670     Goto(&loop);
671   }
672 }
673 
TF_BUILTIN(Subtract,NumberBuiltinsAssembler)674 TF_BUILTIN(Subtract, NumberBuiltinsAssembler) {
675   VARIABLE(var_left, MachineRepresentation::kTagged);
676   VARIABLE(var_right, MachineRepresentation::kTagged);
677   VARIABLE(var_left_double, MachineRepresentation::kFloat64);
678   VARIABLE(var_right_double, MachineRepresentation::kFloat64);
679   Label do_smi_sub(this), do_double_sub(this), do_bigint_sub(this);
680 
681   BinaryOp<Descriptor>(&do_smi_sub, &var_left, &var_right, &do_double_sub,
682                        &var_left_double, &var_right_double, &do_bigint_sub);
683 
684   BIND(&do_smi_sub);
685   {
686     Label if_overflow(this);
687     TNode<Smi> result = TrySmiSub(CAST(var_left.value()),
688                                   CAST(var_right.value()), &if_overflow);
689     Return(result);
690 
691     BIND(&if_overflow);
692     {
693       var_left_double.Bind(SmiToFloat64(var_left.value()));
694       var_right_double.Bind(SmiToFloat64(var_right.value()));
695       Goto(&do_double_sub);
696     }
697   }
698 
699   BIND(&do_double_sub);
700   {
701     Node* value = Float64Sub(var_left_double.value(), var_right_double.value());
702     Return(AllocateHeapNumberWithValue(value));
703   }
704 
705   BIND(&do_bigint_sub);
706   {
707     Node* context = Parameter(Descriptor::kContext);
708     Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
709                        var_right.value(), SmiConstant(Operation::kSubtract)));
710   }
711 }
712 
TF_BUILTIN(BitwiseNot,NumberBuiltinsAssembler)713 TF_BUILTIN(BitwiseNot, NumberBuiltinsAssembler) {
714   Node* context = Parameter(Descriptor::kContext);
715   VARIABLE(var_input, MachineRepresentation::kTagged);
716   Label do_number(this), do_bigint(this);
717 
718   UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
719 
720   BIND(&do_number);
721   {
722     TailCallBuiltin(Builtins::kBitwiseXor, context, var_input.value(),
723                     SmiConstant(-1));
724   }
725 
726   BIND(&do_bigint);
727   {
728     Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
729                        SmiConstant(Operation::kBitwiseNot)));
730   }
731 }
732 
TF_BUILTIN(Decrement,NumberBuiltinsAssembler)733 TF_BUILTIN(Decrement, NumberBuiltinsAssembler) {
734   Node* context = Parameter(Descriptor::kContext);
735   VARIABLE(var_input, MachineRepresentation::kTagged);
736   Label do_number(this), do_bigint(this);
737 
738   UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
739 
740   BIND(&do_number);
741   {
742     TailCallBuiltin(Builtins::kSubtract, context, var_input.value(),
743                     SmiConstant(1));
744   }
745 
746   BIND(&do_bigint);
747   {
748     Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
749                        SmiConstant(Operation::kDecrement)));
750   }
751 }
752 
TF_BUILTIN(Increment,NumberBuiltinsAssembler)753 TF_BUILTIN(Increment, NumberBuiltinsAssembler) {
754   Node* context = Parameter(Descriptor::kContext);
755   VARIABLE(var_input, MachineRepresentation::kTagged);
756   Label do_number(this), do_bigint(this);
757 
758   UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
759 
760   BIND(&do_number);
761   {
762     TailCallBuiltin(Builtins::kAdd, context, var_input.value(), SmiConstant(1));
763   }
764 
765   BIND(&do_bigint);
766   {
767     Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
768                        SmiConstant(Operation::kIncrement)));
769   }
770 }
771 
TF_BUILTIN(Negate,NumberBuiltinsAssembler)772 TF_BUILTIN(Negate, NumberBuiltinsAssembler) {
773   VARIABLE(var_input, MachineRepresentation::kTagged);
774   VARIABLE(var_input_double, MachineRepresentation::kFloat64);
775   Label do_smi(this), do_double(this), do_bigint(this);
776 
777   UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double,
778                       &do_bigint);
779 
780   BIND(&do_smi);
781   { Return(SmiMul(CAST(var_input.value()), SmiConstant(-1))); }
782 
783   BIND(&do_double);
784   {
785     Node* value = Float64Mul(var_input_double.value(), Float64Constant(-1));
786     Return(AllocateHeapNumberWithValue(value));
787   }
788 
789   BIND(&do_bigint);
790   {
791     Node* context = Parameter(Descriptor::kContext);
792     Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
793                        SmiConstant(Operation::kNegate)));
794   }
795 }
796 
TF_BUILTIN(Multiply,NumberBuiltinsAssembler)797 TF_BUILTIN(Multiply, NumberBuiltinsAssembler) {
798   VARIABLE(var_left, MachineRepresentation::kTagged);
799   VARIABLE(var_right, MachineRepresentation::kTagged);
800   VARIABLE(var_left_double, MachineRepresentation::kFloat64);
801   VARIABLE(var_right_double, MachineRepresentation::kFloat64);
802   Label do_smi_mul(this), do_double_mul(this), do_bigint_mul(this);
803 
804   BinaryOp<Descriptor>(&do_smi_mul, &var_left, &var_right, &do_double_mul,
805                        &var_left_double, &var_right_double, &do_bigint_mul);
806 
807   BIND(&do_smi_mul);
808   // The result is not necessarily a smi, in case of overflow.
809   Return(SmiMul(CAST(var_left.value()), CAST(var_right.value())));
810 
811   BIND(&do_double_mul);
812   Node* value = Float64Mul(var_left_double.value(), var_right_double.value());
813   Return(AllocateHeapNumberWithValue(value));
814 
815   BIND(&do_bigint_mul);
816   {
817     Node* context = Parameter(Descriptor::kContext);
818     Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
819                        var_right.value(), SmiConstant(Operation::kMultiply)));
820   }
821 }
822 
TF_BUILTIN(Divide,NumberBuiltinsAssembler)823 TF_BUILTIN(Divide, NumberBuiltinsAssembler) {
824   VARIABLE(var_left, MachineRepresentation::kTagged);
825   VARIABLE(var_right, MachineRepresentation::kTagged);
826   VARIABLE(var_left_double, MachineRepresentation::kFloat64);
827   VARIABLE(var_right_double, MachineRepresentation::kFloat64);
828   Label do_smi_div(this), do_double_div(this), do_bigint_div(this);
829 
830   BinaryOp<Descriptor>(&do_smi_div, &var_left, &var_right, &do_double_div,
831                        &var_left_double, &var_right_double, &do_bigint_div);
832 
833   BIND(&do_smi_div);
834   {
835     // TODO(jkummerow): Consider just always doing a double division.
836     Label bailout(this);
837     TNode<Smi> dividend = CAST(var_left.value());
838     TNode<Smi> divisor = CAST(var_right.value());
839 
840     // Do floating point division if {divisor} is zero.
841     GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout);
842 
843     // Do floating point division if {dividend} is zero and {divisor} is
844     // negative.
845     Label dividend_is_zero(this), dividend_is_not_zero(this);
846     Branch(SmiEqual(dividend, SmiConstant(0)), &dividend_is_zero,
847            &dividend_is_not_zero);
848 
849     BIND(&dividend_is_zero);
850     {
851       GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
852       Goto(&dividend_is_not_zero);
853     }
854     BIND(&dividend_is_not_zero);
855 
856     Node* untagged_divisor = SmiToInt32(divisor);
857     Node* untagged_dividend = SmiToInt32(dividend);
858 
859     // Do floating point division if {dividend} is kMinInt (or kMinInt - 1
860     // if the Smi size is 31) and {divisor} is -1.
861     Label divisor_is_minus_one(this), divisor_is_not_minus_one(this);
862     Branch(Word32Equal(untagged_divisor, Int32Constant(-1)),
863            &divisor_is_minus_one, &divisor_is_not_minus_one);
864 
865     BIND(&divisor_is_minus_one);
866     {
867       GotoIf(Word32Equal(
868                  untagged_dividend,
869                  Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))),
870              &bailout);
871       Goto(&divisor_is_not_minus_one);
872     }
873     BIND(&divisor_is_not_minus_one);
874 
875     // TODO(epertoso): consider adding a machine instruction that returns
876     // both the result and the remainder.
877     Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor);
878     Node* truncated = Int32Mul(untagged_result, untagged_divisor);
879     // Do floating point division if the remainder is not 0.
880     GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout);
881     Return(SmiFromInt32(untagged_result));
882 
883     // Bailout: convert {dividend} and {divisor} to double and do double
884     // division.
885     BIND(&bailout);
886     {
887       var_left_double.Bind(SmiToFloat64(dividend));
888       var_right_double.Bind(SmiToFloat64(divisor));
889       Goto(&do_double_div);
890     }
891   }
892 
893   BIND(&do_double_div);
894   {
895     Node* value = Float64Div(var_left_double.value(), var_right_double.value());
896     Return(AllocateHeapNumberWithValue(value));
897   }
898 
899   BIND(&do_bigint_div);
900   {
901     Node* context = Parameter(Descriptor::kContext);
902     Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
903                        var_right.value(), SmiConstant(Operation::kDivide)));
904   }
905 }
906 
TF_BUILTIN(Modulus,NumberBuiltinsAssembler)907 TF_BUILTIN(Modulus, NumberBuiltinsAssembler) {
908   VARIABLE(var_left, MachineRepresentation::kTagged);
909   VARIABLE(var_right, MachineRepresentation::kTagged);
910   VARIABLE(var_left_double, MachineRepresentation::kFloat64);
911   VARIABLE(var_right_double, MachineRepresentation::kFloat64);
912   Label do_smi_mod(this), do_double_mod(this), do_bigint_mod(this);
913 
914   BinaryOp<Descriptor>(&do_smi_mod, &var_left, &var_right, &do_double_mod,
915                        &var_left_double, &var_right_double, &do_bigint_mod);
916 
917   BIND(&do_smi_mod);
918   Return(SmiMod(CAST(var_left.value()), CAST(var_right.value())));
919 
920   BIND(&do_double_mod);
921   Node* value = Float64Mod(var_left_double.value(), var_right_double.value());
922   Return(AllocateHeapNumberWithValue(value));
923 
924   BIND(&do_bigint_mod);
925   {
926     Node* context = Parameter(Descriptor::kContext);
927     Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
928                        var_right.value(), SmiConstant(Operation::kModulus)));
929   }
930 }
931 
TF_BUILTIN(Exponentiate,NumberBuiltinsAssembler)932 TF_BUILTIN(Exponentiate, NumberBuiltinsAssembler) {
933   VARIABLE(var_left, MachineRepresentation::kTagged);
934   VARIABLE(var_right, MachineRepresentation::kTagged);
935   Label do_number_exp(this), do_bigint_exp(this);
936   Node* context = Parameter(Descriptor::kContext);
937 
938   BinaryOp<Descriptor>(&do_number_exp, &var_left, &var_right, &do_number_exp,
939                        nullptr, nullptr, &do_bigint_exp);
940 
941   BIND(&do_number_exp);
942   {
943     MathBuiltinsAssembler math_asm(state());
944     Return(math_asm.MathPow(context, var_left.value(), var_right.value()));
945   }
946 
947   BIND(&do_bigint_exp);
948   Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
949                      var_right.value(), SmiConstant(Operation::kExponentiate)));
950 }
951 
TF_BUILTIN(ShiftLeft,NumberBuiltinsAssembler)952 TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) {
953   EmitBitwiseOp<Descriptor>(Operation::kShiftLeft);
954 }
955 
TF_BUILTIN(ShiftRight,NumberBuiltinsAssembler)956 TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) {
957   EmitBitwiseOp<Descriptor>(Operation::kShiftRight);
958 }
959 
TF_BUILTIN(ShiftRightLogical,NumberBuiltinsAssembler)960 TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) {
961   EmitBitwiseOp<Descriptor>(Operation::kShiftRightLogical);
962 }
963 
TF_BUILTIN(BitwiseAnd,NumberBuiltinsAssembler)964 TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) {
965   EmitBitwiseOp<Descriptor>(Operation::kBitwiseAnd);
966 }
967 
TF_BUILTIN(BitwiseOr,NumberBuiltinsAssembler)968 TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) {
969   EmitBitwiseOp<Descriptor>(Operation::kBitwiseOr);
970 }
971 
TF_BUILTIN(BitwiseXor,NumberBuiltinsAssembler)972 TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
973   EmitBitwiseOp<Descriptor>(Operation::kBitwiseXor);
974 }
975 
TF_BUILTIN(LessThan,NumberBuiltinsAssembler)976 TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
977   RelationalComparisonBuiltin<Descriptor>(Operation::kLessThan);
978 }
979 
TF_BUILTIN(LessThanOrEqual,NumberBuiltinsAssembler)980 TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) {
981   RelationalComparisonBuiltin<Descriptor>(Operation::kLessThanOrEqual);
982 }
983 
TF_BUILTIN(GreaterThan,NumberBuiltinsAssembler)984 TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) {
985   RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThan);
986 }
987 
TF_BUILTIN(GreaterThanOrEqual,NumberBuiltinsAssembler)988 TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) {
989   RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThanOrEqual);
990 }
991 
TF_BUILTIN(Equal,CodeStubAssembler)992 TF_BUILTIN(Equal, CodeStubAssembler) {
993   Node* lhs = Parameter(Descriptor::kLeft);
994   Node* rhs = Parameter(Descriptor::kRight);
995   Node* context = Parameter(Descriptor::kContext);
996 
997   Return(Equal(lhs, rhs, context));
998 }
999 
TF_BUILTIN(StrictEqual,CodeStubAssembler)1000 TF_BUILTIN(StrictEqual, CodeStubAssembler) {
1001   Node* lhs = Parameter(Descriptor::kLeft);
1002   Node* rhs = Parameter(Descriptor::kRight);
1003 
1004   Return(StrictEqual(lhs, rhs));
1005 }
1006 
1007 }  // namespace internal
1008 }  // namespace v8
1009