• 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/conversions.h"
10 #include "src/counters.h"
11 #include "src/objects-inl.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 class NumberBuiltinsAssembler : public CodeStubAssembler {
17  public:
NumberBuiltinsAssembler(compiler::CodeAssemblerState * state)18   explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state)
19       : CodeStubAssembler(state) {}
20 
21  protected:
22   template <Signedness signed_result = kSigned>
BitwiseOp(std::function<Node * (Node * lhs,Node * rhs)> body)23   void BitwiseOp(std::function<Node*(Node* lhs, Node* rhs)> body) {
24     Node* left = Parameter(0);
25     Node* right = Parameter(1);
26     Node* context = Parameter(2);
27 
28     Node* lhs_value = TruncateTaggedToWord32(context, left);
29     Node* rhs_value = TruncateTaggedToWord32(context, right);
30     Node* value = body(lhs_value, rhs_value);
31     Node* result = signed_result == kSigned ? ChangeInt32ToTagged(value)
32                                             : ChangeUint32ToTagged(value);
33     Return(result);
34   }
35 
36   template <Signedness signed_result = kSigned>
BitwiseShiftOp(std::function<Node * (Node * lhs,Node * shift_count)> body)37   void BitwiseShiftOp(std::function<Node*(Node* lhs, Node* shift_count)> body) {
38     BitwiseOp<signed_result>([this, body](Node* lhs, Node* rhs) {
39       Node* shift_count = Word32And(rhs, Int32Constant(0x1f));
40       return body(lhs, shift_count);
41     });
42   }
43 
RelationalComparisonBuiltin(RelationalComparisonMode mode)44   void RelationalComparisonBuiltin(RelationalComparisonMode mode) {
45     Node* lhs = Parameter(0);
46     Node* rhs = Parameter(1);
47     Node* context = Parameter(2);
48 
49     Return(RelationalComparison(mode, lhs, rhs, context));
50   }
51 };
52 
53 // -----------------------------------------------------------------------------
54 // ES6 section 20.1 Number Objects
55 
56 // ES6 section 20.1.2.2 Number.isFinite ( number )
TF_BUILTIN(NumberIsFinite,CodeStubAssembler)57 TF_BUILTIN(NumberIsFinite, CodeStubAssembler) {
58   Node* number = Parameter(1);
59 
60   Label return_true(this), return_false(this);
61 
62   // Check if {number} is a Smi.
63   GotoIf(TaggedIsSmi(number), &return_true);
64 
65   // Check if {number} is a HeapNumber.
66   GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false);
67 
68   // Check if {number} contains a finite, non-NaN value.
69   Node* number_value = LoadHeapNumberValue(number);
70   BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false,
71                        &return_true);
72 
73   Bind(&return_true);
74   Return(BooleanConstant(true));
75 
76   Bind(&return_false);
77   Return(BooleanConstant(false));
78 }
79 
80 // ES6 section 20.1.2.3 Number.isInteger ( number )
TF_BUILTIN(NumberIsInteger,CodeStubAssembler)81 TF_BUILTIN(NumberIsInteger, CodeStubAssembler) {
82   Node* number = Parameter(1);
83 
84   Label return_true(this), return_false(this);
85 
86   // Check if {number} is a Smi.
87   GotoIf(TaggedIsSmi(number), &return_true);
88 
89   // Check if {number} is a HeapNumber.
90   GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false);
91 
92   // Load the actual value of {number}.
93   Node* number_value = LoadHeapNumberValue(number);
94 
95   // Truncate the value of {number} to an integer (or an infinity).
96   Node* integer = Float64Trunc(number_value);
97 
98   // Check if {number}s value matches the integer (ruling out the infinities).
99   Branch(Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)),
100          &return_true, &return_false);
101 
102   Bind(&return_true);
103   Return(BooleanConstant(true));
104 
105   Bind(&return_false);
106   Return(BooleanConstant(false));
107 }
108 
109 // ES6 section 20.1.2.4 Number.isNaN ( number )
TF_BUILTIN(NumberIsNaN,CodeStubAssembler)110 TF_BUILTIN(NumberIsNaN, CodeStubAssembler) {
111   Node* number = Parameter(1);
112 
113   Label return_true(this), return_false(this);
114 
115   // Check if {number} is a Smi.
116   GotoIf(TaggedIsSmi(number), &return_false);
117 
118   // Check if {number} is a HeapNumber.
119   GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false);
120 
121   // Check if {number} contains a NaN value.
122   Node* number_value = LoadHeapNumberValue(number);
123   BranchIfFloat64IsNaN(number_value, &return_true, &return_false);
124 
125   Bind(&return_true);
126   Return(BooleanConstant(true));
127 
128   Bind(&return_false);
129   Return(BooleanConstant(false));
130 }
131 
132 // ES6 section 20.1.2.5 Number.isSafeInteger ( number )
TF_BUILTIN(NumberIsSafeInteger,CodeStubAssembler)133 TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) {
134   Node* number = Parameter(1);
135 
136   Label return_true(this), return_false(this);
137 
138   // Check if {number} is a Smi.
139   GotoIf(TaggedIsSmi(number), &return_true);
140 
141   // Check if {number} is a HeapNumber.
142   GotoIfNot(IsHeapNumberMap(LoadMap(number)), &return_false);
143 
144   // Load the actual value of {number}.
145   Node* number_value = LoadHeapNumberValue(number);
146 
147   // Truncate the value of {number} to an integer (or an infinity).
148   Node* integer = Float64Trunc(number_value);
149 
150   // Check if {number}s value matches the integer (ruling out the infinities).
151   GotoIfNot(
152       Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)),
153       &return_false);
154 
155   // Check if the {integer} value is in safe integer range.
156   Branch(Float64LessThanOrEqual(Float64Abs(integer),
157                                 Float64Constant(kMaxSafeInteger)),
158          &return_true, &return_false);
159 
160   Bind(&return_true);
161   Return(BooleanConstant(true));
162 
163   Bind(&return_false);
164   Return(BooleanConstant(false));
165 }
166 
167 // ES6 section 20.1.2.12 Number.parseFloat ( string )
TF_BUILTIN(NumberParseFloat,CodeStubAssembler)168 TF_BUILTIN(NumberParseFloat, CodeStubAssembler) {
169   Node* context = Parameter(4);
170 
171   // We might need to loop once for ToString conversion.
172   Variable var_input(this, MachineRepresentation::kTagged);
173   Label loop(this, &var_input);
174   var_input.Bind(Parameter(1));
175   Goto(&loop);
176   Bind(&loop);
177   {
178     // Load the current {input} value.
179     Node* input = var_input.value();
180 
181     // Check if the {input} is a HeapObject or a Smi.
182     Label if_inputissmi(this), if_inputisnotsmi(this);
183     Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi);
184 
185     Bind(&if_inputissmi);
186     {
187       // The {input} is already a Number, no need to do anything.
188       Return(input);
189     }
190 
191     Bind(&if_inputisnotsmi);
192     {
193       // The {input} is a HeapObject, check if it's already a String.
194       Label if_inputisstring(this), if_inputisnotstring(this);
195       Node* input_map = LoadMap(input);
196       Node* input_instance_type = LoadMapInstanceType(input_map);
197       Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
198              &if_inputisnotstring);
199 
200       Bind(&if_inputisstring);
201       {
202         // The {input} is already a String, check if {input} contains
203         // a cached array index.
204         Label if_inputcached(this), if_inputnotcached(this);
205         Node* input_hash = LoadNameHashField(input);
206         Node* input_bit = Word32And(
207             input_hash, Int32Constant(String::kContainsCachedArrayIndexMask));
208         Branch(Word32Equal(input_bit, Int32Constant(0)), &if_inputcached,
209                &if_inputnotcached);
210 
211         Bind(&if_inputcached);
212         {
213           // Just return the {input}s cached array index.
214           Node* input_array_index =
215               DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
216           Return(SmiTag(input_array_index));
217         }
218 
219         Bind(&if_inputnotcached);
220         {
221           // Need to fall back to the runtime to convert {input} to double.
222           Return(CallRuntime(Runtime::kStringParseFloat, context, input));
223         }
224       }
225 
226       Bind(&if_inputisnotstring);
227       {
228         // The {input} is neither a String nor a Smi, check for HeapNumber.
229         Label if_inputisnumber(this),
230             if_inputisnotnumber(this, Label::kDeferred);
231         Branch(IsHeapNumberMap(input_map), &if_inputisnumber,
232                &if_inputisnotnumber);
233 
234         Bind(&if_inputisnumber);
235         {
236           // The {input} is already a Number, take care of -0.
237           Label if_inputiszero(this), if_inputisnotzero(this);
238           Node* input_value = LoadHeapNumberValue(input);
239           Branch(Float64Equal(input_value, Float64Constant(0.0)),
240                  &if_inputiszero, &if_inputisnotzero);
241 
242           Bind(&if_inputiszero);
243           Return(SmiConstant(0));
244 
245           Bind(&if_inputisnotzero);
246           Return(input);
247         }
248 
249         Bind(&if_inputisnotnumber);
250         {
251           // Need to convert the {input} to String first.
252           // TODO(bmeurer): This could be more efficient if necessary.
253           Callable callable = CodeFactory::ToString(isolate());
254           var_input.Bind(CallStub(callable, context, input));
255           Goto(&loop);
256         }
257       }
258     }
259   }
260 }
261 
262 // ES6 section 20.1.2.13 Number.parseInt ( string, radix )
TF_BUILTIN(NumberParseInt,CodeStubAssembler)263 TF_BUILTIN(NumberParseInt, CodeStubAssembler) {
264   Node* input = Parameter(1);
265   Node* radix = Parameter(2);
266   Node* context = Parameter(5);
267 
268   // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10).
269   Label if_radix10(this), if_generic(this, Label::kDeferred);
270   GotoIf(WordEqual(radix, UndefinedConstant()), &if_radix10);
271   GotoIf(WordEqual(radix, SmiConstant(Smi::FromInt(10))), &if_radix10);
272   GotoIf(WordEqual(radix, SmiConstant(Smi::FromInt(0))), &if_radix10);
273   Goto(&if_generic);
274 
275   Bind(&if_radix10);
276   {
277     // Check if we can avoid the ToString conversion on {input}.
278     Label if_inputissmi(this), if_inputisheapnumber(this),
279         if_inputisstring(this);
280     GotoIf(TaggedIsSmi(input), &if_inputissmi);
281     Node* input_map = LoadMap(input);
282     GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber);
283     Node* input_instance_type = LoadMapInstanceType(input_map);
284     Branch(IsStringInstanceType(input_instance_type), &if_inputisstring,
285            &if_generic);
286 
287     Bind(&if_inputissmi);
288     {
289       // Just return the {input}.
290       Return(input);
291     }
292 
293     Bind(&if_inputisheapnumber);
294     {
295       // Check if the {input} value is in Signed32 range.
296       Label if_inputissigned32(this);
297       Node* input_value = LoadHeapNumberValue(input);
298       Node* input_value32 = TruncateFloat64ToWord32(input_value);
299       GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)),
300              &if_inputissigned32);
301 
302       // Check if the absolute {input} value is in the ]0.01,1e9[ range.
303       Node* input_value_abs = Float64Abs(input_value);
304 
305       GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1e9)),
306                 &if_generic);
307       Branch(Float64LessThan(Float64Constant(0.01), input_value_abs),
308              &if_inputissigned32, &if_generic);
309 
310       // Return the truncated int32 value, and return the tagged result.
311       Bind(&if_inputissigned32);
312       Node* result = ChangeInt32ToTagged(input_value32);
313       Return(result);
314     }
315 
316     Bind(&if_inputisstring);
317     {
318       // Check if the String {input} has a cached array index.
319       Node* input_hash = LoadNameHashField(input);
320       Node* input_bit = Word32And(
321           input_hash, Int32Constant(String::kContainsCachedArrayIndexMask));
322       GotoIf(Word32NotEqual(input_bit, Int32Constant(0)), &if_generic);
323 
324       // Return the cached array index as result.
325       Node* input_index =
326           DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash);
327       Node* result = SmiTag(input_index);
328       Return(result);
329     }
330   }
331 
332   Bind(&if_generic);
333   {
334     Node* result = CallRuntime(Runtime::kStringParseInt, context, input, radix);
335     Return(result);
336   }
337 }
338 
339 // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
BUILTIN(NumberPrototypeToExponential)340 BUILTIN(NumberPrototypeToExponential) {
341   HandleScope scope(isolate);
342   Handle<Object> value = args.at(0);
343   Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1);
344 
345   // Unwrap the receiver {value}.
346   if (value->IsJSValue()) {
347     value = handle(Handle<JSValue>::cast(value)->value(), isolate);
348   }
349   if (!value->IsNumber()) {
350     THROW_NEW_ERROR_RETURN_FAILURE(
351         isolate, NewTypeError(MessageTemplate::kNotGeneric,
352                               isolate->factory()->NewStringFromAsciiChecked(
353                                   "Number.prototype.toExponential")));
354   }
355   double const value_number = value->Number();
356 
357   // Convert the {fraction_digits} to an integer first.
358   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
359       isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits));
360   double const fraction_digits_number = fraction_digits->Number();
361 
362   if (std::isnan(value_number)) return isolate->heap()->nan_string();
363   if (std::isinf(value_number)) {
364     return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
365                                 : isolate->heap()->infinity_string();
366   }
367   if (fraction_digits_number < 0.0 || fraction_digits_number > 20.0) {
368     THROW_NEW_ERROR_RETURN_FAILURE(
369         isolate, NewRangeError(MessageTemplate::kNumberFormatRange,
370                                isolate->factory()->NewStringFromAsciiChecked(
371                                    "toExponential()")));
372   }
373   int const f = args.atOrUndefined(isolate, 1)->IsUndefined(isolate)
374                     ? -1
375                     : static_cast<int>(fraction_digits_number);
376   char* const str = DoubleToExponentialCString(value_number, f);
377   Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
378   DeleteArray(str);
379   return *result;
380 }
381 
382 // ES6 section 20.1.3.3 Number.prototype.toFixed ( fractionDigits )
BUILTIN(NumberPrototypeToFixed)383 BUILTIN(NumberPrototypeToFixed) {
384   HandleScope scope(isolate);
385   Handle<Object> value = args.at(0);
386   Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1);
387 
388   // Unwrap the receiver {value}.
389   if (value->IsJSValue()) {
390     value = handle(Handle<JSValue>::cast(value)->value(), isolate);
391   }
392   if (!value->IsNumber()) {
393     THROW_NEW_ERROR_RETURN_FAILURE(
394         isolate, NewTypeError(MessageTemplate::kNotGeneric,
395                               isolate->factory()->NewStringFromAsciiChecked(
396                                   "Number.prototype.toFixed")));
397   }
398   double const value_number = value->Number();
399 
400   // Convert the {fraction_digits} to an integer first.
401   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
402       isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits));
403   double const fraction_digits_number = fraction_digits->Number();
404 
405   // Check if the {fraction_digits} are in the supported range.
406   if (fraction_digits_number < 0.0 || fraction_digits_number > 20.0) {
407     THROW_NEW_ERROR_RETURN_FAILURE(
408         isolate, NewRangeError(MessageTemplate::kNumberFormatRange,
409                                isolate->factory()->NewStringFromAsciiChecked(
410                                    "toFixed() digits")));
411   }
412 
413   if (std::isnan(value_number)) return isolate->heap()->nan_string();
414   if (std::isinf(value_number)) {
415     return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
416                                 : isolate->heap()->infinity_string();
417   }
418   char* const str = DoubleToFixedCString(
419       value_number, static_cast<int>(fraction_digits_number));
420   Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
421   DeleteArray(str);
422   return *result;
423 }
424 
425 // ES6 section 20.1.3.4 Number.prototype.toLocaleString ( [ r1 [ , r2 ] ] )
BUILTIN(NumberPrototypeToLocaleString)426 BUILTIN(NumberPrototypeToLocaleString) {
427   HandleScope scope(isolate);
428   Handle<Object> value = args.at(0);
429 
430   // Unwrap the receiver {value}.
431   if (value->IsJSValue()) {
432     value = handle(Handle<JSValue>::cast(value)->value(), isolate);
433   }
434   if (!value->IsNumber()) {
435     THROW_NEW_ERROR_RETURN_FAILURE(
436         isolate, NewTypeError(MessageTemplate::kNotGeneric,
437                               isolate->factory()->NewStringFromAsciiChecked(
438                                   "Number.prototype.toLocaleString")));
439   }
440 
441   // Turn the {value} into a String.
442   return *isolate->factory()->NumberToString(value);
443 }
444 
445 // ES6 section 20.1.3.5 Number.prototype.toPrecision ( precision )
BUILTIN(NumberPrototypeToPrecision)446 BUILTIN(NumberPrototypeToPrecision) {
447   HandleScope scope(isolate);
448   Handle<Object> value = args.at(0);
449   Handle<Object> precision = args.atOrUndefined(isolate, 1);
450 
451   // Unwrap the receiver {value}.
452   if (value->IsJSValue()) {
453     value = handle(Handle<JSValue>::cast(value)->value(), isolate);
454   }
455   if (!value->IsNumber()) {
456     THROW_NEW_ERROR_RETURN_FAILURE(
457         isolate, NewTypeError(MessageTemplate::kNotGeneric,
458                               isolate->factory()->NewStringFromAsciiChecked(
459                                   "Number.prototype.toPrecision")));
460   }
461   double const value_number = value->Number();
462 
463   // If no {precision} was specified, just return ToString of {value}.
464   if (precision->IsUndefined(isolate)) {
465     return *isolate->factory()->NumberToString(value);
466   }
467 
468   // Convert the {precision} to an integer first.
469   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, precision,
470                                      Object::ToInteger(isolate, precision));
471   double const precision_number = precision->Number();
472 
473   if (std::isnan(value_number)) return isolate->heap()->nan_string();
474   if (std::isinf(value_number)) {
475     return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
476                                 : isolate->heap()->infinity_string();
477   }
478   if (precision_number < 1.0 || precision_number > 21.0) {
479     THROW_NEW_ERROR_RETURN_FAILURE(
480         isolate, NewRangeError(MessageTemplate::kToPrecisionFormatRange));
481   }
482   char* const str = DoubleToPrecisionCString(
483       value_number, static_cast<int>(precision_number));
484   Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
485   DeleteArray(str);
486   return *result;
487 }
488 
489 // ES6 section 20.1.3.6 Number.prototype.toString ( [ radix ] )
BUILTIN(NumberPrototypeToString)490 BUILTIN(NumberPrototypeToString) {
491   HandleScope scope(isolate);
492   Handle<Object> value = args.at(0);
493   Handle<Object> radix = args.atOrUndefined(isolate, 1);
494 
495   // Unwrap the receiver {value}.
496   if (value->IsJSValue()) {
497     value = handle(Handle<JSValue>::cast(value)->value(), isolate);
498   }
499   if (!value->IsNumber()) {
500     THROW_NEW_ERROR_RETURN_FAILURE(
501         isolate, NewTypeError(MessageTemplate::kNotGeneric,
502                               isolate->factory()->NewStringFromAsciiChecked(
503                                   "Number.prototype.toString")));
504   }
505   double const value_number = value->Number();
506 
507   // If no {radix} was specified, just return ToString of {value}.
508   if (radix->IsUndefined(isolate)) {
509     return *isolate->factory()->NumberToString(value);
510   }
511 
512   // Convert the {radix} to an integer first.
513   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix,
514                                      Object::ToInteger(isolate, radix));
515   double const radix_number = radix->Number();
516 
517   // If {radix} is 10, just return ToString of {value}.
518   if (radix_number == 10.0) return *isolate->factory()->NumberToString(value);
519 
520   // Make sure the {radix} is within the valid range.
521   if (radix_number < 2.0 || radix_number > 36.0) {
522     THROW_NEW_ERROR_RETURN_FAILURE(
523         isolate, NewRangeError(MessageTemplate::kToRadixFormatRange));
524   }
525 
526   // Fast case where the result is a one character string.
527   if ((IsUint32Double(value_number) && value_number < radix_number) ||
528       value_number == -0.0) {
529     // Character array used for conversion.
530     static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
531     return *isolate->factory()->LookupSingleCharacterStringFromCode(
532         kCharTable[static_cast<uint32_t>(value_number)]);
533   }
534 
535   // Slow case.
536   if (std::isnan(value_number)) return isolate->heap()->nan_string();
537   if (std::isinf(value_number)) {
538     return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
539                                 : isolate->heap()->infinity_string();
540   }
541   char* const str =
542       DoubleToRadixCString(value_number, static_cast<int>(radix_number));
543   Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
544   DeleteArray(str);
545   return *result;
546 }
547 
548 // ES6 section 20.1.3.7 Number.prototype.valueOf ( )
TF_BUILTIN(NumberPrototypeValueOf,CodeStubAssembler)549 TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) {
550   Node* receiver = Parameter(0);
551   Node* context = Parameter(3);
552 
553   Node* result = ToThisValue(context, receiver, PrimitiveType::kNumber,
554                              "Number.prototype.valueOf");
555   Return(result);
556 }
557 
TF_BUILTIN(Add,CodeStubAssembler)558 TF_BUILTIN(Add, CodeStubAssembler) {
559   Node* left = Parameter(0);
560   Node* right = Parameter(1);
561   Node* context = Parameter(2);
562 
563   // Shared entry for floating point addition.
564   Label do_fadd(this);
565   Variable var_fadd_lhs(this, MachineRepresentation::kFloat64),
566       var_fadd_rhs(this, MachineRepresentation::kFloat64);
567 
568   // We might need to loop several times due to ToPrimitive, ToString and/or
569   // ToNumber conversions.
570   Variable var_lhs(this, MachineRepresentation::kTagged),
571       var_rhs(this, MachineRepresentation::kTagged),
572       var_result(this, MachineRepresentation::kTagged);
573   Variable* loop_vars[2] = {&var_lhs, &var_rhs};
574   Label loop(this, 2, loop_vars), end(this),
575       string_add_convert_left(this, Label::kDeferred),
576       string_add_convert_right(this, Label::kDeferred);
577   var_lhs.Bind(left);
578   var_rhs.Bind(right);
579   Goto(&loop);
580   Bind(&loop);
581   {
582     // Load the current {lhs} and {rhs} values.
583     Node* lhs = var_lhs.value();
584     Node* rhs = var_rhs.value();
585 
586     // Check if the {lhs} is a Smi or a HeapObject.
587     Label if_lhsissmi(this), if_lhsisnotsmi(this);
588     Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);
589 
590     Bind(&if_lhsissmi);
591     {
592       // Check if the {rhs} is also a Smi.
593       Label if_rhsissmi(this), if_rhsisnotsmi(this);
594       Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
595 
596       Bind(&if_rhsissmi);
597       {
598         // Try fast Smi addition first.
599         Node* pair = IntPtrAddWithOverflow(BitcastTaggedToWord(lhs),
600                                            BitcastTaggedToWord(rhs));
601         Node* overflow = Projection(1, pair);
602 
603         // Check if the Smi additon overflowed.
604         Label if_overflow(this), if_notoverflow(this);
605         Branch(overflow, &if_overflow, &if_notoverflow);
606 
607         Bind(&if_overflow);
608         {
609           var_fadd_lhs.Bind(SmiToFloat64(lhs));
610           var_fadd_rhs.Bind(SmiToFloat64(rhs));
611           Goto(&do_fadd);
612         }
613 
614         Bind(&if_notoverflow);
615         var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair)));
616         Goto(&end);
617       }
618 
619       Bind(&if_rhsisnotsmi);
620       {
621         // Load the map of {rhs}.
622         Node* rhs_map = LoadMap(rhs);
623 
624         // Check if the {rhs} is a HeapNumber.
625         Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred);
626         Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber);
627 
628         Bind(&if_rhsisnumber);
629         {
630           var_fadd_lhs.Bind(SmiToFloat64(lhs));
631           var_fadd_rhs.Bind(LoadHeapNumberValue(rhs));
632           Goto(&do_fadd);
633         }
634 
635         Bind(&if_rhsisnotnumber);
636         {
637           // Load the instance type of {rhs}.
638           Node* rhs_instance_type = LoadMapInstanceType(rhs_map);
639 
640           // Check if the {rhs} is a String.
641           Label if_rhsisstring(this, Label::kDeferred),
642               if_rhsisnotstring(this, Label::kDeferred);
643           Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring,
644                  &if_rhsisnotstring);
645 
646           Bind(&if_rhsisstring);
647           {
648             var_lhs.Bind(lhs);
649             var_rhs.Bind(rhs);
650             Goto(&string_add_convert_left);
651           }
652 
653           Bind(&if_rhsisnotstring);
654           {
655             // Check if {rhs} is a JSReceiver.
656             Label if_rhsisreceiver(this, Label::kDeferred),
657                 if_rhsisnotreceiver(this, Label::kDeferred);
658             Branch(IsJSReceiverInstanceType(rhs_instance_type),
659                    &if_rhsisreceiver, &if_rhsisnotreceiver);
660 
661             Bind(&if_rhsisreceiver);
662             {
663               // Convert {rhs} to a primitive first passing no hint.
664               Callable callable =
665                   CodeFactory::NonPrimitiveToPrimitive(isolate());
666               var_rhs.Bind(CallStub(callable, context, rhs));
667               Goto(&loop);
668             }
669 
670             Bind(&if_rhsisnotreceiver);
671             {
672               // Convert {rhs} to a Number first.
673               Callable callable = CodeFactory::NonNumberToNumber(isolate());
674               var_rhs.Bind(CallStub(callable, context, rhs));
675               Goto(&loop);
676             }
677           }
678         }
679       }
680     }
681 
682     Bind(&if_lhsisnotsmi);
683     {
684       // Load the map and instance type of {lhs}.
685       Node* lhs_instance_type = LoadInstanceType(lhs);
686 
687       // Check if {lhs} is a String.
688       Label if_lhsisstring(this), if_lhsisnotstring(this);
689       Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring,
690              &if_lhsisnotstring);
691 
692       Bind(&if_lhsisstring);
693       {
694         var_lhs.Bind(lhs);
695         var_rhs.Bind(rhs);
696         Goto(&string_add_convert_right);
697       }
698 
699       Bind(&if_lhsisnotstring);
700       {
701         // Check if {rhs} is a Smi.
702         Label if_rhsissmi(this), if_rhsisnotsmi(this);
703         Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
704 
705         Bind(&if_rhsissmi);
706         {
707           // Check if {lhs} is a Number.
708           Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred);
709           Branch(
710               Word32Equal(lhs_instance_type, Int32Constant(HEAP_NUMBER_TYPE)),
711               &if_lhsisnumber, &if_lhsisnotnumber);
712 
713           Bind(&if_lhsisnumber);
714           {
715             // The {lhs} is a HeapNumber, the {rhs} is a Smi, just add them.
716             var_fadd_lhs.Bind(LoadHeapNumberValue(lhs));
717             var_fadd_rhs.Bind(SmiToFloat64(rhs));
718             Goto(&do_fadd);
719           }
720 
721           Bind(&if_lhsisnotnumber);
722           {
723             // The {lhs} is neither a Number nor a String, and the {rhs} is a
724             // Smi.
725             Label if_lhsisreceiver(this, Label::kDeferred),
726                 if_lhsisnotreceiver(this, Label::kDeferred);
727             Branch(IsJSReceiverInstanceType(lhs_instance_type),
728                    &if_lhsisreceiver, &if_lhsisnotreceiver);
729 
730             Bind(&if_lhsisreceiver);
731             {
732               // Convert {lhs} to a primitive first passing no hint.
733               Callable callable =
734                   CodeFactory::NonPrimitiveToPrimitive(isolate());
735               var_lhs.Bind(CallStub(callable, context, lhs));
736               Goto(&loop);
737             }
738 
739             Bind(&if_lhsisnotreceiver);
740             {
741               // Convert {lhs} to a Number first.
742               Callable callable = CodeFactory::NonNumberToNumber(isolate());
743               var_lhs.Bind(CallStub(callable, context, lhs));
744               Goto(&loop);
745             }
746           }
747         }
748 
749         Bind(&if_rhsisnotsmi);
750         {
751           // Load the instance type of {rhs}.
752           Node* rhs_instance_type = LoadInstanceType(rhs);
753 
754           // Check if {rhs} is a String.
755           Label if_rhsisstring(this), if_rhsisnotstring(this);
756           Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring,
757                  &if_rhsisnotstring);
758 
759           Bind(&if_rhsisstring);
760           {
761             var_lhs.Bind(lhs);
762             var_rhs.Bind(rhs);
763             Goto(&string_add_convert_left);
764           }
765 
766           Bind(&if_rhsisnotstring);
767           {
768             // Check if {lhs} is a HeapNumber.
769             Label if_lhsisnumber(this), if_lhsisnotnumber(this);
770             Branch(
771                 Word32Equal(lhs_instance_type, Int32Constant(HEAP_NUMBER_TYPE)),
772                 &if_lhsisnumber, &if_lhsisnotnumber);
773 
774             Bind(&if_lhsisnumber);
775             {
776               // Check if {rhs} is also a HeapNumber.
777               Label if_rhsisnumber(this),
778                   if_rhsisnotnumber(this, Label::kDeferred);
779               Branch(Word32Equal(rhs_instance_type,
780                                  Int32Constant(HEAP_NUMBER_TYPE)),
781                      &if_rhsisnumber, &if_rhsisnotnumber);
782 
783               Bind(&if_rhsisnumber);
784               {
785                 // Perform a floating point addition.
786                 var_fadd_lhs.Bind(LoadHeapNumberValue(lhs));
787                 var_fadd_rhs.Bind(LoadHeapNumberValue(rhs));
788                 Goto(&do_fadd);
789               }
790 
791               Bind(&if_rhsisnotnumber);
792               {
793                 // Check if {rhs} is a JSReceiver.
794                 Label if_rhsisreceiver(this, Label::kDeferred),
795                     if_rhsisnotreceiver(this, Label::kDeferred);
796                 Branch(IsJSReceiverInstanceType(rhs_instance_type),
797                        &if_rhsisreceiver, &if_rhsisnotreceiver);
798 
799                 Bind(&if_rhsisreceiver);
800                 {
801                   // Convert {rhs} to a primitive first passing no hint.
802                   Callable callable =
803                       CodeFactory::NonPrimitiveToPrimitive(isolate());
804                   var_rhs.Bind(CallStub(callable, context, rhs));
805                   Goto(&loop);
806                 }
807 
808                 Bind(&if_rhsisnotreceiver);
809                 {
810                   // Convert {rhs} to a Number first.
811                   Callable callable = CodeFactory::NonNumberToNumber(isolate());
812                   var_rhs.Bind(CallStub(callable, context, rhs));
813                   Goto(&loop);
814                 }
815               }
816             }
817 
818             Bind(&if_lhsisnotnumber);
819             {
820               // Check if {lhs} is a JSReceiver.
821               Label if_lhsisreceiver(this, Label::kDeferred),
822                   if_lhsisnotreceiver(this);
823               Branch(IsJSReceiverInstanceType(lhs_instance_type),
824                      &if_lhsisreceiver, &if_lhsisnotreceiver);
825 
826               Bind(&if_lhsisreceiver);
827               {
828                 // Convert {lhs} to a primitive first passing no hint.
829                 Callable callable =
830                     CodeFactory::NonPrimitiveToPrimitive(isolate());
831                 var_lhs.Bind(CallStub(callable, context, lhs));
832                 Goto(&loop);
833               }
834 
835               Bind(&if_lhsisnotreceiver);
836               {
837                 // Check if {rhs} is a JSReceiver.
838                 Label if_rhsisreceiver(this, Label::kDeferred),
839                     if_rhsisnotreceiver(this, Label::kDeferred);
840                 Branch(IsJSReceiverInstanceType(rhs_instance_type),
841                        &if_rhsisreceiver, &if_rhsisnotreceiver);
842 
843                 Bind(&if_rhsisreceiver);
844                 {
845                   // Convert {rhs} to a primitive first passing no hint.
846                   Callable callable =
847                       CodeFactory::NonPrimitiveToPrimitive(isolate());
848                   var_rhs.Bind(CallStub(callable, context, rhs));
849                   Goto(&loop);
850                 }
851 
852                 Bind(&if_rhsisnotreceiver);
853                 {
854                   // Convert {lhs} to a Number first.
855                   Callable callable = CodeFactory::NonNumberToNumber(isolate());
856                   var_lhs.Bind(CallStub(callable, context, lhs));
857                   Goto(&loop);
858                 }
859               }
860             }
861           }
862         }
863       }
864     }
865   }
866   Bind(&string_add_convert_left);
867   {
868     // Convert {lhs}, which is a Smi, to a String and concatenate the
869     // resulting string with the String {rhs}.
870     Callable callable =
871         CodeFactory::StringAdd(isolate(), STRING_ADD_CONVERT_LEFT, NOT_TENURED);
872     var_result.Bind(
873         CallStub(callable, context, var_lhs.value(), var_rhs.value()));
874     Goto(&end);
875   }
876 
877   Bind(&string_add_convert_right);
878   {
879     // Convert {lhs}, which is a Smi, to a String and concatenate the
880     // resulting string with the String {rhs}.
881     Callable callable = CodeFactory::StringAdd(
882         isolate(), STRING_ADD_CONVERT_RIGHT, NOT_TENURED);
883     var_result.Bind(
884         CallStub(callable, context, var_lhs.value(), var_rhs.value()));
885     Goto(&end);
886   }
887 
888   Bind(&do_fadd);
889   {
890     Node* lhs_value = var_fadd_lhs.value();
891     Node* rhs_value = var_fadd_rhs.value();
892     Node* value = Float64Add(lhs_value, rhs_value);
893     Node* result = AllocateHeapNumberWithValue(value);
894     var_result.Bind(result);
895     Goto(&end);
896   }
897   Bind(&end);
898   Return(var_result.value());
899 }
900 
TF_BUILTIN(Subtract,CodeStubAssembler)901 TF_BUILTIN(Subtract, CodeStubAssembler) {
902   Node* left = Parameter(0);
903   Node* right = Parameter(1);
904   Node* context = Parameter(2);
905 
906   // Shared entry for floating point subtraction.
907   Label do_fsub(this), end(this);
908   Variable var_fsub_lhs(this, MachineRepresentation::kFloat64),
909       var_fsub_rhs(this, MachineRepresentation::kFloat64);
910 
911   // We might need to loop several times due to ToPrimitive and/or ToNumber
912   // conversions.
913   Variable var_lhs(this, MachineRepresentation::kTagged),
914       var_rhs(this, MachineRepresentation::kTagged),
915       var_result(this, MachineRepresentation::kTagged);
916   Variable* loop_vars[2] = {&var_lhs, &var_rhs};
917   Label loop(this, 2, loop_vars);
918   var_lhs.Bind(left);
919   var_rhs.Bind(right);
920   Goto(&loop);
921   Bind(&loop);
922   {
923     // Load the current {lhs} and {rhs} values.
924     Node* lhs = var_lhs.value();
925     Node* rhs = var_rhs.value();
926 
927     // Check if the {lhs} is a Smi or a HeapObject.
928     Label if_lhsissmi(this), if_lhsisnotsmi(this);
929     Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);
930 
931     Bind(&if_lhsissmi);
932     {
933       // Check if the {rhs} is also a Smi.
934       Label if_rhsissmi(this), if_rhsisnotsmi(this);
935       Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
936 
937       Bind(&if_rhsissmi);
938       {
939         // Try a fast Smi subtraction first.
940         Node* pair = IntPtrSubWithOverflow(BitcastTaggedToWord(lhs),
941                                            BitcastTaggedToWord(rhs));
942         Node* overflow = Projection(1, pair);
943 
944         // Check if the Smi subtraction overflowed.
945         Label if_overflow(this), if_notoverflow(this);
946         Branch(overflow, &if_overflow, &if_notoverflow);
947 
948         Bind(&if_overflow);
949         {
950           // The result doesn't fit into Smi range.
951           var_fsub_lhs.Bind(SmiToFloat64(lhs));
952           var_fsub_rhs.Bind(SmiToFloat64(rhs));
953           Goto(&do_fsub);
954         }
955 
956         Bind(&if_notoverflow);
957         var_result.Bind(BitcastWordToTaggedSigned(Projection(0, pair)));
958         Goto(&end);
959       }
960 
961       Bind(&if_rhsisnotsmi);
962       {
963         // Load the map of the {rhs}.
964         Node* rhs_map = LoadMap(rhs);
965 
966         // Check if {rhs} is a HeapNumber.
967         Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred);
968         Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber);
969 
970         Bind(&if_rhsisnumber);
971         {
972           // Perform a floating point subtraction.
973           var_fsub_lhs.Bind(SmiToFloat64(lhs));
974           var_fsub_rhs.Bind(LoadHeapNumberValue(rhs));
975           Goto(&do_fsub);
976         }
977 
978         Bind(&if_rhsisnotnumber);
979         {
980           // Convert the {rhs} to a Number first.
981           Callable callable = CodeFactory::NonNumberToNumber(isolate());
982           var_rhs.Bind(CallStub(callable, context, rhs));
983           Goto(&loop);
984         }
985       }
986     }
987 
988     Bind(&if_lhsisnotsmi);
989     {
990       // Load the map of the {lhs}.
991       Node* lhs_map = LoadMap(lhs);
992 
993       // Check if the {lhs} is a HeapNumber.
994       Label if_lhsisnumber(this), if_lhsisnotnumber(this, Label::kDeferred);
995       Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber);
996 
997       Bind(&if_lhsisnumber);
998       {
999         // Check if the {rhs} is a Smi.
1000         Label if_rhsissmi(this), if_rhsisnotsmi(this);
1001         Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
1002 
1003         Bind(&if_rhsissmi);
1004         {
1005           // Perform a floating point subtraction.
1006           var_fsub_lhs.Bind(LoadHeapNumberValue(lhs));
1007           var_fsub_rhs.Bind(SmiToFloat64(rhs));
1008           Goto(&do_fsub);
1009         }
1010 
1011         Bind(&if_rhsisnotsmi);
1012         {
1013           // Load the map of the {rhs}.
1014           Node* rhs_map = LoadMap(rhs);
1015 
1016           // Check if the {rhs} is a HeapNumber.
1017           Label if_rhsisnumber(this), if_rhsisnotnumber(this, Label::kDeferred);
1018           Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber);
1019 
1020           Bind(&if_rhsisnumber);
1021           {
1022             // Perform a floating point subtraction.
1023             var_fsub_lhs.Bind(LoadHeapNumberValue(lhs));
1024             var_fsub_rhs.Bind(LoadHeapNumberValue(rhs));
1025             Goto(&do_fsub);
1026           }
1027 
1028           Bind(&if_rhsisnotnumber);
1029           {
1030             // Convert the {rhs} to a Number first.
1031             Callable callable = CodeFactory::NonNumberToNumber(isolate());
1032             var_rhs.Bind(CallStub(callable, context, rhs));
1033             Goto(&loop);
1034           }
1035         }
1036       }
1037 
1038       Bind(&if_lhsisnotnumber);
1039       {
1040         // Convert the {lhs} to a Number first.
1041         Callable callable = CodeFactory::NonNumberToNumber(isolate());
1042         var_lhs.Bind(CallStub(callable, context, lhs));
1043         Goto(&loop);
1044       }
1045     }
1046   }
1047 
1048   Bind(&do_fsub);
1049   {
1050     Node* lhs_value = var_fsub_lhs.value();
1051     Node* rhs_value = var_fsub_rhs.value();
1052     Node* value = Float64Sub(lhs_value, rhs_value);
1053     var_result.Bind(AllocateHeapNumberWithValue(value));
1054     Goto(&end);
1055   }
1056   Bind(&end);
1057   Return(var_result.value());
1058 }
1059 
TF_BUILTIN(Multiply,CodeStubAssembler)1060 TF_BUILTIN(Multiply, CodeStubAssembler) {
1061   Node* left = Parameter(0);
1062   Node* right = Parameter(1);
1063   Node* context = Parameter(2);
1064 
1065   // Shared entry point for floating point multiplication.
1066   Label do_fmul(this), return_result(this);
1067   Variable var_lhs_float64(this, MachineRepresentation::kFloat64),
1068       var_rhs_float64(this, MachineRepresentation::kFloat64);
1069 
1070   // We might need to loop one or two times due to ToNumber conversions.
1071   Variable var_lhs(this, MachineRepresentation::kTagged),
1072       var_rhs(this, MachineRepresentation::kTagged),
1073       var_result(this, MachineRepresentation::kTagged);
1074   Variable* loop_variables[] = {&var_lhs, &var_rhs};
1075   Label loop(this, 2, loop_variables);
1076   var_lhs.Bind(left);
1077   var_rhs.Bind(right);
1078   Goto(&loop);
1079   Bind(&loop);
1080   {
1081     Node* lhs = var_lhs.value();
1082     Node* rhs = var_rhs.value();
1083 
1084     Label lhs_is_smi(this), lhs_is_not_smi(this);
1085     Branch(TaggedIsSmi(lhs), &lhs_is_smi, &lhs_is_not_smi);
1086 
1087     Bind(&lhs_is_smi);
1088     {
1089       Label rhs_is_smi(this), rhs_is_not_smi(this);
1090       Branch(TaggedIsSmi(rhs), &rhs_is_smi, &rhs_is_not_smi);
1091 
1092       Bind(&rhs_is_smi);
1093       {
1094         // Both {lhs} and {rhs} are Smis. The result is not necessarily a smi,
1095         // in case of overflow.
1096         var_result.Bind(SmiMul(lhs, rhs));
1097         Goto(&return_result);
1098       }
1099 
1100       Bind(&rhs_is_not_smi);
1101       {
1102         Node* rhs_map = LoadMap(rhs);
1103 
1104         // Check if {rhs} is a HeapNumber.
1105         Label rhs_is_number(this), rhs_is_not_number(this, Label::kDeferred);
1106         Branch(IsHeapNumberMap(rhs_map), &rhs_is_number, &rhs_is_not_number);
1107 
1108         Bind(&rhs_is_number);
1109         {
1110           // Convert {lhs} to a double and multiply it with the value of {rhs}.
1111           var_lhs_float64.Bind(SmiToFloat64(lhs));
1112           var_rhs_float64.Bind(LoadHeapNumberValue(rhs));
1113           Goto(&do_fmul);
1114         }
1115 
1116         Bind(&rhs_is_not_number);
1117         {
1118           // Multiplication is commutative, swap {lhs} with {rhs} and loop.
1119           var_lhs.Bind(rhs);
1120           var_rhs.Bind(lhs);
1121           Goto(&loop);
1122         }
1123       }
1124     }
1125 
1126     Bind(&lhs_is_not_smi);
1127     {
1128       Node* lhs_map = LoadMap(lhs);
1129 
1130       // Check if {lhs} is a HeapNumber.
1131       Label lhs_is_number(this), lhs_is_not_number(this, Label::kDeferred);
1132       Branch(IsHeapNumberMap(lhs_map), &lhs_is_number, &lhs_is_not_number);
1133 
1134       Bind(&lhs_is_number);
1135       {
1136         // Check if {rhs} is a Smi.
1137         Label rhs_is_smi(this), rhs_is_not_smi(this);
1138         Branch(TaggedIsSmi(rhs), &rhs_is_smi, &rhs_is_not_smi);
1139 
1140         Bind(&rhs_is_smi);
1141         {
1142           // Convert {rhs} to a double and multiply it with the value of {lhs}.
1143           var_lhs_float64.Bind(LoadHeapNumberValue(lhs));
1144           var_rhs_float64.Bind(SmiToFloat64(rhs));
1145           Goto(&do_fmul);
1146         }
1147 
1148         Bind(&rhs_is_not_smi);
1149         {
1150           Node* rhs_map = LoadMap(rhs);
1151 
1152           // Check if {rhs} is a HeapNumber.
1153           Label rhs_is_number(this), rhs_is_not_number(this, Label::kDeferred);
1154           Branch(IsHeapNumberMap(rhs_map), &rhs_is_number, &rhs_is_not_number);
1155 
1156           Bind(&rhs_is_number);
1157           {
1158             // Both {lhs} and {rhs} are HeapNumbers. Load their values and
1159             // multiply them.
1160             var_lhs_float64.Bind(LoadHeapNumberValue(lhs));
1161             var_rhs_float64.Bind(LoadHeapNumberValue(rhs));
1162             Goto(&do_fmul);
1163           }
1164 
1165           Bind(&rhs_is_not_number);
1166           {
1167             // Multiplication is commutative, swap {lhs} with {rhs} and loop.
1168             var_lhs.Bind(rhs);
1169             var_rhs.Bind(lhs);
1170             Goto(&loop);
1171           }
1172         }
1173       }
1174 
1175       Bind(&lhs_is_not_number);
1176       {
1177         // Convert {lhs} to a Number and loop.
1178         Callable callable = CodeFactory::NonNumberToNumber(isolate());
1179         var_lhs.Bind(CallStub(callable, context, lhs));
1180         Goto(&loop);
1181       }
1182     }
1183   }
1184 
1185   Bind(&do_fmul);
1186   {
1187     Node* value = Float64Mul(var_lhs_float64.value(), var_rhs_float64.value());
1188     Node* result = AllocateHeapNumberWithValue(value);
1189     var_result.Bind(result);
1190     Goto(&return_result);
1191   }
1192 
1193   Bind(&return_result);
1194   Return(var_result.value());
1195 }
1196 
TF_BUILTIN(Divide,CodeStubAssembler)1197 TF_BUILTIN(Divide, CodeStubAssembler) {
1198   Node* left = Parameter(0);
1199   Node* right = Parameter(1);
1200   Node* context = Parameter(2);
1201 
1202   // Shared entry point for floating point division.
1203   Label do_fdiv(this), end(this);
1204   Variable var_dividend_float64(this, MachineRepresentation::kFloat64),
1205       var_divisor_float64(this, MachineRepresentation::kFloat64);
1206 
1207   // We might need to loop one or two times due to ToNumber conversions.
1208   Variable var_dividend(this, MachineRepresentation::kTagged),
1209       var_divisor(this, MachineRepresentation::kTagged),
1210       var_result(this, MachineRepresentation::kTagged);
1211   Variable* loop_variables[] = {&var_dividend, &var_divisor};
1212   Label loop(this, 2, loop_variables);
1213   var_dividend.Bind(left);
1214   var_divisor.Bind(right);
1215   Goto(&loop);
1216   Bind(&loop);
1217   {
1218     Node* dividend = var_dividend.value();
1219     Node* divisor = var_divisor.value();
1220 
1221     Label dividend_is_smi(this), dividend_is_not_smi(this);
1222     Branch(TaggedIsSmi(dividend), &dividend_is_smi, &dividend_is_not_smi);
1223 
1224     Bind(&dividend_is_smi);
1225     {
1226       Label divisor_is_smi(this), divisor_is_not_smi(this);
1227       Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi);
1228 
1229       Bind(&divisor_is_smi);
1230       {
1231         Label bailout(this);
1232 
1233         // Do floating point division if {divisor} is zero.
1234         GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout);
1235 
1236         // Do floating point division {dividend} is zero and {divisor} is
1237         // negative.
1238         Label dividend_is_zero(this), dividend_is_not_zero(this);
1239         Branch(SmiEqual(dividend, SmiConstant(0)), &dividend_is_zero,
1240                &dividend_is_not_zero);
1241 
1242         Bind(&dividend_is_zero);
1243         {
1244           GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
1245           Goto(&dividend_is_not_zero);
1246         }
1247         Bind(&dividend_is_not_zero);
1248 
1249         Node* untagged_divisor = SmiToWord32(divisor);
1250         Node* untagged_dividend = SmiToWord32(dividend);
1251 
1252         // Do floating point division if {dividend} is kMinInt (or kMinInt - 1
1253         // if the Smi size is 31) and {divisor} is -1.
1254         Label divisor_is_minus_one(this), divisor_is_not_minus_one(this);
1255         Branch(Word32Equal(untagged_divisor, Int32Constant(-1)),
1256                &divisor_is_minus_one, &divisor_is_not_minus_one);
1257 
1258         Bind(&divisor_is_minus_one);
1259         {
1260           GotoIf(
1261               Word32Equal(untagged_dividend,
1262                           Int32Constant(kSmiValueSize == 32 ? kMinInt
1263                                                             : (kMinInt >> 1))),
1264               &bailout);
1265           Goto(&divisor_is_not_minus_one);
1266         }
1267         Bind(&divisor_is_not_minus_one);
1268 
1269         // TODO(epertoso): consider adding a machine instruction that returns
1270         // both the result and the remainder.
1271         Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor);
1272         Node* truncated = Int32Mul(untagged_result, untagged_divisor);
1273         // Do floating point division if the remainder is not 0.
1274         GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout);
1275         var_result.Bind(SmiFromWord32(untagged_result));
1276         Goto(&end);
1277 
1278         // Bailout: convert {dividend} and {divisor} to double and do double
1279         // division.
1280         Bind(&bailout);
1281         {
1282           var_dividend_float64.Bind(SmiToFloat64(dividend));
1283           var_divisor_float64.Bind(SmiToFloat64(divisor));
1284           Goto(&do_fdiv);
1285         }
1286       }
1287 
1288       Bind(&divisor_is_not_smi);
1289       {
1290         Node* divisor_map = LoadMap(divisor);
1291 
1292         // Check if {divisor} is a HeapNumber.
1293         Label divisor_is_number(this),
1294             divisor_is_not_number(this, Label::kDeferred);
1295         Branch(IsHeapNumberMap(divisor_map), &divisor_is_number,
1296                &divisor_is_not_number);
1297 
1298         Bind(&divisor_is_number);
1299         {
1300           // Convert {dividend} to a double and divide it with the value of
1301           // {divisor}.
1302           var_dividend_float64.Bind(SmiToFloat64(dividend));
1303           var_divisor_float64.Bind(LoadHeapNumberValue(divisor));
1304           Goto(&do_fdiv);
1305         }
1306 
1307         Bind(&divisor_is_not_number);
1308         {
1309           // Convert {divisor} to a number and loop.
1310           Callable callable = CodeFactory::NonNumberToNumber(isolate());
1311           var_divisor.Bind(CallStub(callable, context, divisor));
1312           Goto(&loop);
1313         }
1314       }
1315     }
1316 
1317     Bind(&dividend_is_not_smi);
1318     {
1319       Node* dividend_map = LoadMap(dividend);
1320 
1321       // Check if {dividend} is a HeapNumber.
1322       Label dividend_is_number(this),
1323           dividend_is_not_number(this, Label::kDeferred);
1324       Branch(IsHeapNumberMap(dividend_map), &dividend_is_number,
1325              &dividend_is_not_number);
1326 
1327       Bind(&dividend_is_number);
1328       {
1329         // Check if {divisor} is a Smi.
1330         Label divisor_is_smi(this), divisor_is_not_smi(this);
1331         Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi);
1332 
1333         Bind(&divisor_is_smi);
1334         {
1335           // Convert {divisor} to a double and use it for a floating point
1336           // division.
1337           var_dividend_float64.Bind(LoadHeapNumberValue(dividend));
1338           var_divisor_float64.Bind(SmiToFloat64(divisor));
1339           Goto(&do_fdiv);
1340         }
1341 
1342         Bind(&divisor_is_not_smi);
1343         {
1344           Node* divisor_map = LoadMap(divisor);
1345 
1346           // Check if {divisor} is a HeapNumber.
1347           Label divisor_is_number(this),
1348               divisor_is_not_number(this, Label::kDeferred);
1349           Branch(IsHeapNumberMap(divisor_map), &divisor_is_number,
1350                  &divisor_is_not_number);
1351 
1352           Bind(&divisor_is_number);
1353           {
1354             // Both {dividend} and {divisor} are HeapNumbers. Load their values
1355             // and divide them.
1356             var_dividend_float64.Bind(LoadHeapNumberValue(dividend));
1357             var_divisor_float64.Bind(LoadHeapNumberValue(divisor));
1358             Goto(&do_fdiv);
1359           }
1360 
1361           Bind(&divisor_is_not_number);
1362           {
1363             // Convert {divisor} to a number and loop.
1364             Callable callable = CodeFactory::NonNumberToNumber(isolate());
1365             var_divisor.Bind(CallStub(callable, context, divisor));
1366             Goto(&loop);
1367           }
1368         }
1369       }
1370 
1371       Bind(&dividend_is_not_number);
1372       {
1373         // Convert {dividend} to a Number and loop.
1374         Callable callable = CodeFactory::NonNumberToNumber(isolate());
1375         var_dividend.Bind(CallStub(callable, context, dividend));
1376         Goto(&loop);
1377       }
1378     }
1379   }
1380 
1381   Bind(&do_fdiv);
1382   {
1383     Node* value =
1384         Float64Div(var_dividend_float64.value(), var_divisor_float64.value());
1385     var_result.Bind(AllocateHeapNumberWithValue(value));
1386     Goto(&end);
1387   }
1388   Bind(&end);
1389   Return(var_result.value());
1390 }
1391 
TF_BUILTIN(Modulus,CodeStubAssembler)1392 TF_BUILTIN(Modulus, CodeStubAssembler) {
1393   Node* left = Parameter(0);
1394   Node* right = Parameter(1);
1395   Node* context = Parameter(2);
1396 
1397   Variable var_result(this, MachineRepresentation::kTagged);
1398   Label return_result(this, &var_result);
1399 
1400   // Shared entry point for floating point modulus.
1401   Label do_fmod(this);
1402   Variable var_dividend_float64(this, MachineRepresentation::kFloat64),
1403       var_divisor_float64(this, MachineRepresentation::kFloat64);
1404 
1405   // We might need to loop one or two times due to ToNumber conversions.
1406   Variable var_dividend(this, MachineRepresentation::kTagged),
1407       var_divisor(this, MachineRepresentation::kTagged);
1408   Variable* loop_variables[] = {&var_dividend, &var_divisor};
1409   Label loop(this, 2, loop_variables);
1410   var_dividend.Bind(left);
1411   var_divisor.Bind(right);
1412   Goto(&loop);
1413   Bind(&loop);
1414   {
1415     Node* dividend = var_dividend.value();
1416     Node* divisor = var_divisor.value();
1417 
1418     Label dividend_is_smi(this), dividend_is_not_smi(this);
1419     Branch(TaggedIsSmi(dividend), &dividend_is_smi, &dividend_is_not_smi);
1420 
1421     Bind(&dividend_is_smi);
1422     {
1423       Label dividend_is_not_zero(this);
1424       Label divisor_is_smi(this), divisor_is_not_smi(this);
1425       Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi);
1426 
1427       Bind(&divisor_is_smi);
1428       {
1429         // Compute the modulus of two Smis.
1430         var_result.Bind(SmiMod(dividend, divisor));
1431         Goto(&return_result);
1432       }
1433 
1434       Bind(&divisor_is_not_smi);
1435       {
1436         Node* divisor_map = LoadMap(divisor);
1437 
1438         // Check if {divisor} is a HeapNumber.
1439         Label divisor_is_number(this),
1440             divisor_is_not_number(this, Label::kDeferred);
1441         Branch(IsHeapNumberMap(divisor_map), &divisor_is_number,
1442                &divisor_is_not_number);
1443 
1444         Bind(&divisor_is_number);
1445         {
1446           // Convert {dividend} to a double and compute its modulus with the
1447           // value of {dividend}.
1448           var_dividend_float64.Bind(SmiToFloat64(dividend));
1449           var_divisor_float64.Bind(LoadHeapNumberValue(divisor));
1450           Goto(&do_fmod);
1451         }
1452 
1453         Bind(&divisor_is_not_number);
1454         {
1455           // Convert {divisor} to a number and loop.
1456           Callable callable = CodeFactory::NonNumberToNumber(isolate());
1457           var_divisor.Bind(CallStub(callable, context, divisor));
1458           Goto(&loop);
1459         }
1460       }
1461     }
1462 
1463     Bind(&dividend_is_not_smi);
1464     {
1465       Node* dividend_map = LoadMap(dividend);
1466 
1467       // Check if {dividend} is a HeapNumber.
1468       Label dividend_is_number(this),
1469           dividend_is_not_number(this, Label::kDeferred);
1470       Branch(IsHeapNumberMap(dividend_map), &dividend_is_number,
1471              &dividend_is_not_number);
1472 
1473       Bind(&dividend_is_number);
1474       {
1475         // Check if {divisor} is a Smi.
1476         Label divisor_is_smi(this), divisor_is_not_smi(this);
1477         Branch(TaggedIsSmi(divisor), &divisor_is_smi, &divisor_is_not_smi);
1478 
1479         Bind(&divisor_is_smi);
1480         {
1481           // Convert {divisor} to a double and compute {dividend}'s modulus with
1482           // it.
1483           var_dividend_float64.Bind(LoadHeapNumberValue(dividend));
1484           var_divisor_float64.Bind(SmiToFloat64(divisor));
1485           Goto(&do_fmod);
1486         }
1487 
1488         Bind(&divisor_is_not_smi);
1489         {
1490           Node* divisor_map = LoadMap(divisor);
1491 
1492           // Check if {divisor} is a HeapNumber.
1493           Label divisor_is_number(this),
1494               divisor_is_not_number(this, Label::kDeferred);
1495           Branch(IsHeapNumberMap(divisor_map), &divisor_is_number,
1496                  &divisor_is_not_number);
1497 
1498           Bind(&divisor_is_number);
1499           {
1500             // Both {dividend} and {divisor} are HeapNumbers. Load their values
1501             // and compute their modulus.
1502             var_dividend_float64.Bind(LoadHeapNumberValue(dividend));
1503             var_divisor_float64.Bind(LoadHeapNumberValue(divisor));
1504             Goto(&do_fmod);
1505           }
1506 
1507           Bind(&divisor_is_not_number);
1508           {
1509             // Convert {divisor} to a number and loop.
1510             Callable callable = CodeFactory::NonNumberToNumber(isolate());
1511             var_divisor.Bind(CallStub(callable, context, divisor));
1512             Goto(&loop);
1513           }
1514         }
1515       }
1516 
1517       Bind(&dividend_is_not_number);
1518       {
1519         // Convert {dividend} to a Number and loop.
1520         Callable callable = CodeFactory::NonNumberToNumber(isolate());
1521         var_dividend.Bind(CallStub(callable, context, dividend));
1522         Goto(&loop);
1523       }
1524     }
1525   }
1526 
1527   Bind(&do_fmod);
1528   {
1529     Node* value =
1530         Float64Mod(var_dividend_float64.value(), var_divisor_float64.value());
1531     var_result.Bind(AllocateHeapNumberWithValue(value));
1532     Goto(&return_result);
1533   }
1534 
1535   Bind(&return_result);
1536   Return(var_result.value());
1537 }
1538 
TF_BUILTIN(ShiftLeft,NumberBuiltinsAssembler)1539 TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) {
1540   BitwiseShiftOp([this](Node* lhs, Node* shift_count) {
1541     return Word32Shl(lhs, shift_count);
1542   });
1543 }
1544 
TF_BUILTIN(ShiftRight,NumberBuiltinsAssembler)1545 TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) {
1546   BitwiseShiftOp([this](Node* lhs, Node* shift_count) {
1547     return Word32Sar(lhs, shift_count);
1548   });
1549 }
1550 
TF_BUILTIN(ShiftRightLogical,NumberBuiltinsAssembler)1551 TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) {
1552   BitwiseShiftOp<kUnsigned>([this](Node* lhs, Node* shift_count) {
1553     return Word32Shr(lhs, shift_count);
1554   });
1555 }
1556 
TF_BUILTIN(BitwiseAnd,NumberBuiltinsAssembler)1557 TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) {
1558   BitwiseOp([this](Node* lhs, Node* rhs) { return Word32And(lhs, rhs); });
1559 }
1560 
TF_BUILTIN(BitwiseOr,NumberBuiltinsAssembler)1561 TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) {
1562   BitwiseOp([this](Node* lhs, Node* rhs) { return Word32Or(lhs, rhs); });
1563 }
1564 
TF_BUILTIN(BitwiseXor,NumberBuiltinsAssembler)1565 TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
1566   BitwiseOp([this](Node* lhs, Node* rhs) { return Word32Xor(lhs, rhs); });
1567 }
1568 
TF_BUILTIN(LessThan,NumberBuiltinsAssembler)1569 TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
1570   RelationalComparisonBuiltin(kLessThan);
1571 }
1572 
TF_BUILTIN(LessThanOrEqual,NumberBuiltinsAssembler)1573 TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) {
1574   RelationalComparisonBuiltin(kLessThanOrEqual);
1575 }
1576 
TF_BUILTIN(GreaterThan,NumberBuiltinsAssembler)1577 TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) {
1578   RelationalComparisonBuiltin(kGreaterThan);
1579 }
1580 
TF_BUILTIN(GreaterThanOrEqual,NumberBuiltinsAssembler)1581 TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) {
1582   RelationalComparisonBuiltin(kGreaterThanOrEqual);
1583 }
1584 
TF_BUILTIN(Equal,CodeStubAssembler)1585 TF_BUILTIN(Equal, CodeStubAssembler) {
1586   Node* lhs = Parameter(0);
1587   Node* rhs = Parameter(1);
1588   Node* context = Parameter(2);
1589 
1590   Return(Equal(kDontNegateResult, lhs, rhs, context));
1591 }
1592 
TF_BUILTIN(NotEqual,CodeStubAssembler)1593 TF_BUILTIN(NotEqual, CodeStubAssembler) {
1594   Node* lhs = Parameter(0);
1595   Node* rhs = Parameter(1);
1596   Node* context = Parameter(2);
1597 
1598   Return(Equal(kNegateResult, lhs, rhs, context));
1599 }
1600 
TF_BUILTIN(StrictEqual,CodeStubAssembler)1601 TF_BUILTIN(StrictEqual, CodeStubAssembler) {
1602   Node* lhs = Parameter(0);
1603   Node* rhs = Parameter(1);
1604   Node* context = Parameter(2);
1605 
1606   Return(StrictEqual(kDontNegateResult, lhs, rhs, context));
1607 }
1608 
TF_BUILTIN(StrictNotEqual,CodeStubAssembler)1609 TF_BUILTIN(StrictNotEqual, CodeStubAssembler) {
1610   Node* lhs = Parameter(0);
1611   Node* rhs = Parameter(1);
1612   Node* context = Parameter(2);
1613 
1614   Return(StrictEqual(kNegateResult, lhs, rhs, context));
1615 }
1616 
1617 }  // namespace internal
1618 }  // namespace v8
1619