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
11namespace v8 {
12namespace internal {
13
14// -----------------------------------------------------------------------------
15// ES6 section 20.1 Number Objects
16
17class NumberBuiltinsAssembler : public CodeStubAssembler {
18 public:
19 explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state)
20 : CodeStubAssembler(state) {}
21
22 protected:
23 template <typename Descriptor>
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>
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
74TF_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
97TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) {
98 Node* result = AllocateHeapNumber();
99 Return(result);
100}
101
102// ES6 #sec-number.isinteger
103TF_BUILTIN(NumberIsInteger, CodeStubAssembler) {
104 TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
105 Return(SelectBooleanConstant(IsInteger(number)));
106}
107
108// ES6 #sec-number.isnan
109TF_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
132TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) {
133 TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
134 Return(SelectBooleanConstant(IsSafeInteger(number)));
135}
136
137// ES6 #sec-number.parsefloat
138TF_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
231TF_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
309TF_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
317TF_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
326class AddStubAssembler : public CodeStubAssembler {
327 public:
328 explicit AddStubAssembler(compiler::CodeAssemblerState* state)
329 : CodeStubAssembler(state) {}
330
331 protected:
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
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
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
359TF_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 TailCallBuiltin(Builtins::kStringAdd_ConvertLeft, context, var_left.value(),
529 var_right.value());
530 }
531
532 BIND(&string_add_convert_right);
533 {
534 // Convert {right} to a String and concatenate it with the String {left}.
535 TailCallBuiltin(Builtins::kStringAdd_ConvertRight, context,
536 var_left.value(), var_right.value());
537 }
538
539 BIND(&do_bigint_add);
540 {
541 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
542 var_right.value(), SmiConstant(Operation::kAdd)));
543 }
544
545 BIND(&do_double_add);
546 {
547 Node* value = Float64Add(var_left_double.value(), var_right_double.value());
548 Return(AllocateHeapNumberWithValue(value));
549 }
550}
551
552template <typename Descriptor>
553void NumberBuiltinsAssembler::UnaryOp(Variable* var_input, Label* do_smi,
554 Label* do_double,
555 Variable* var_input_double,
556 Label* do_bigint) {
557 DCHECK_EQ(var_input->rep(), MachineRepresentation::kTagged);
558 DCHECK_IMPLIES(var_input_double != nullptr,
559 var_input_double->rep() == MachineRepresentation::kFloat64);
560
561 Node* context = Parameter(Descriptor::kContext);
562 var_input->Bind(Parameter(Descriptor::kValue));
563
564 // We might need to loop for ToNumeric conversion.
565 Label loop(this, {var_input});
566 Goto(&loop);
567 BIND(&loop);
568 Node* input = var_input->value();
569
570 Label not_number(this);
571 GotoIf(TaggedIsSmi(input), do_smi);
572 GotoIfNot(IsHeapNumber(input), &not_number);
573 if (var_input_double != nullptr) {
574 var_input_double->Bind(LoadHeapNumberValue(input));
575 }
576 Goto(do_double);
577
578 BIND(&not_number);
579 GotoIf(IsBigInt(input), do_bigint);
580 var_input->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, input));
581 Goto(&loop);
582}
583
584template <typename Descriptor>
585void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left,
586 Variable* var_right, Label* doubles,
587 Variable* var_left_double,
588 Variable* var_right_double,
589 Label* bigints) {
590 DCHECK_EQ(var_left->rep(), MachineRepresentation::kTagged);
591 DCHECK_EQ(var_right->rep(), MachineRepresentation::kTagged);
592 DCHECK_IMPLIES(var_left_double != nullptr,
593 var_left_double->rep() == MachineRepresentation::kFloat64);
594 DCHECK_IMPLIES(var_right_double != nullptr,
595 var_right_double->rep() == MachineRepresentation::kFloat64);
596 DCHECK_EQ(var_left_double == nullptr, var_right_double == nullptr);
597
598 Node* context = Parameter(Descriptor::kContext);
599 var_left->Bind(Parameter(Descriptor::kLeft));
600 var_right->Bind(Parameter(Descriptor::kRight));
601
602 // We might need to loop for ToNumeric conversions.
603 Label loop(this, {var_left, var_right});
604 Goto(&loop);
605 BIND(&loop);
606
607 Label left_not_smi(this), right_not_smi(this);
608 Label left_not_number(this), right_not_number(this);
609 GotoIfNot(TaggedIsSmi(var_left->value()), &left_not_smi);
610 GotoIf(TaggedIsSmi(var_right->value()), smis);
611
612 // At this point, var_left is a Smi but var_right is not.
613 GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
614 if (var_left_double != nullptr) {
615 var_left_double->Bind(SmiToFloat64(var_left->value()));
616 var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
617 }
618 Goto(doubles);
619
620 BIND(&left_not_smi);
621 {
622 GotoIfNot(IsHeapNumber(var_left->value()), &left_not_number);
623 GotoIfNot(TaggedIsSmi(var_right->value()), &right_not_smi);
624
625 // At this point, var_left is a HeapNumber and var_right is a Smi.
626 if (var_left_double != nullptr) {
627 var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
628 var_right_double->Bind(SmiToFloat64(var_right->value()));
629 }
630 Goto(doubles);
631 }
632
633 BIND(&right_not_smi);
634 {
635 GotoIfNot(IsHeapNumber(var_right->value()), &right_not_number);
636 if (var_left_double != nullptr) {
637 var_left_double->Bind(LoadHeapNumberValue(var_left->value()));
638 var_right_double->Bind(LoadHeapNumberValue(var_right->value()));
639 }
640 Goto(doubles);
641 }
642
643 BIND(&left_not_number);
644 {
645 Label left_bigint(this);
646 GotoIf(IsBigInt(var_left->value()), &left_bigint);
647 var_left->Bind(
648 CallBuiltin(Builtins::kNonNumberToNumeric, context, var_left->value()));
649 Goto(&loop);
650
651 BIND(&left_bigint);
652 {
653 // Jump to {bigints} if {var_right} is already a Numeric.
654 GotoIf(TaggedIsSmi(var_right->value()), bigints);
655 GotoIf(IsBigInt(var_right->value()), bigints);
656 GotoIf(IsHeapNumber(var_right->value()), bigints);
657 var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
658 var_right->value()));
659 Goto(&loop);
660 }
661 }
662
663 BIND(&right_not_number);
664 {
665 GotoIf(IsBigInt(var_right->value()), bigints);
666 var_right->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context,
667 var_right->value()));
668 Goto(&loop);
669 }
670}
671
672TF_BUILTIN(Subtract, NumberBuiltinsAssembler) {
673 VARIABLE(var_left, MachineRepresentation::kTagged);
674 VARIABLE(var_right, MachineRepresentation::kTagged);
675 VARIABLE(var_left_double, MachineRepresentation::kFloat64);
676 VARIABLE(var_right_double, MachineRepresentation::kFloat64);
677 Label do_smi_sub(this), do_double_sub(this), do_bigint_sub(this);
678
679 BinaryOp<Descriptor>(&do_smi_sub, &var_left, &var_right, &do_double_sub,
680 &var_left_double, &var_right_double, &do_bigint_sub);
681
682 BIND(&do_smi_sub);
683 {
684 Label if_overflow(this);
685 TNode<Smi> result = TrySmiSub(CAST(var_left.value()),
686 CAST(var_right.value()), &if_overflow);
687 Return(result);
688
689 BIND(&if_overflow);
690 {
691 var_left_double.Bind(SmiToFloat64(var_left.value()));
692 var_right_double.Bind(SmiToFloat64(var_right.value()));
693 Goto(&do_double_sub);
694 }
695 }
696
697 BIND(&do_double_sub);
698 {
699 Node* value = Float64Sub(var_left_double.value(), var_right_double.value());
700 Return(AllocateHeapNumberWithValue(value));
701 }
702
703 BIND(&do_bigint_sub);
704 {
705 Node* context = Parameter(Descriptor::kContext);
706 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
707 var_right.value(), SmiConstant(Operation::kSubtract)));
708 }
709}
710
711TF_BUILTIN(BitwiseNot, NumberBuiltinsAssembler) {
712 Node* context = Parameter(Descriptor::kContext);
713 VARIABLE(var_input, MachineRepresentation::kTagged);
714 Label do_number(this), do_bigint(this);
715
716 UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
717
718 BIND(&do_number);
719 {
720 TailCallBuiltin(Builtins::kBitwiseXor, context, var_input.value(),
721 SmiConstant(-1));
722 }
723
724 BIND(&do_bigint);
725 {
726 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
727 SmiConstant(Operation::kBitwiseNot)));
728 }
729}
730
731TF_BUILTIN(Decrement, NumberBuiltinsAssembler) {
732 Node* context = Parameter(Descriptor::kContext);
733 VARIABLE(var_input, MachineRepresentation::kTagged);
734 Label do_number(this), do_bigint(this);
735
736 UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
737
738 BIND(&do_number);
739 {
740 TailCallBuiltin(Builtins::kSubtract, context, var_input.value(),
741 SmiConstant(1));
742 }
743
744 BIND(&do_bigint);
745 {
746 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
747 SmiConstant(Operation::kDecrement)));
748 }
749}
750
751TF_BUILTIN(Increment, NumberBuiltinsAssembler) {
752 Node* context = Parameter(Descriptor::kContext);
753 VARIABLE(var_input, MachineRepresentation::kTagged);
754 Label do_number(this), do_bigint(this);
755
756 UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint);
757
758 BIND(&do_number);
759 {
760 TailCallBuiltin(Builtins::kAdd, context, var_input.value(), SmiConstant(1));
761 }
762
763 BIND(&do_bigint);
764 {
765 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
766 SmiConstant(Operation::kIncrement)));
767 }
768}
769
770TF_BUILTIN(Negate, NumberBuiltinsAssembler) {
771 VARIABLE(var_input, MachineRepresentation::kTagged);
772 VARIABLE(var_input_double, MachineRepresentation::kFloat64);
773 Label do_smi(this), do_double(this), do_bigint(this);
774
775 UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double,
776 &do_bigint);
777
778 BIND(&do_smi);
779 { Return(SmiMul(CAST(var_input.value()), SmiConstant(-1))); }
780
781 BIND(&do_double);
782 {
783 Node* value = Float64Mul(var_input_double.value(), Float64Constant(-1));
784 Return(AllocateHeapNumberWithValue(value));
785 }
786
787 BIND(&do_bigint);
788 {
789 Node* context = Parameter(Descriptor::kContext);
790 Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
791 SmiConstant(Operation::kNegate)));
792 }
793}
794
795TF_BUILTIN(Multiply, NumberBuiltinsAssembler) {
796 VARIABLE(var_left, MachineRepresentation::kTagged);
797 VARIABLE(var_right, MachineRepresentation::kTagged);
798 VARIABLE(var_left_double, MachineRepresentation::kFloat64);
799 VARIABLE(var_right_double, MachineRepresentation::kFloat64);
800 Label do_smi_mul(this), do_double_mul(this), do_bigint_mul(this);
801
802 BinaryOp<Descriptor>(&do_smi_mul, &var_left, &var_right, &do_double_mul,
803 &var_left_double, &var_right_double, &do_bigint_mul);
804
805 BIND(&do_smi_mul);
806 // The result is not necessarily a smi, in case of overflow.
807 Return(SmiMul(CAST(var_left.value()), CAST(var_right.value())));
808
809 BIND(&do_double_mul);
810 Node* value = Float64Mul(var_left_double.value(), var_right_double.value());
811 Return(AllocateHeapNumberWithValue(value));
812
813 BIND(&do_bigint_mul);
814 {
815 Node* context = Parameter(Descriptor::kContext);
816 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
817 var_right.value(), SmiConstant(Operation::kMultiply)));
818 }
819}
820
821TF_BUILTIN(Divide, NumberBuiltinsAssembler) {
822 VARIABLE(var_left, MachineRepresentation::kTagged);
823 VARIABLE(var_right, MachineRepresentation::kTagged);
824 VARIABLE(var_left_double, MachineRepresentation::kFloat64);
825 VARIABLE(var_right_double, MachineRepresentation::kFloat64);
826 Label do_smi_div(this), do_double_div(this), do_bigint_div(this);
827
828 BinaryOp<Descriptor>(&do_smi_div, &var_left, &var_right, &do_double_div,
829 &var_left_double, &var_right_double, &do_bigint_div);
830
831 BIND(&do_smi_div);
832 {
833 // TODO(jkummerow): Consider just always doing a double division.
834 Label bailout(this);
835 TNode<Smi> dividend = CAST(var_left.value());
836 TNode<Smi> divisor = CAST(var_right.value());
837
838 // Do floating point division if {divisor} is zero.
839 GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout);
840
841 // Do floating point division if {dividend} is zero and {divisor} is
842 // negative.
843 Label dividend_is_zero(this), dividend_is_not_zero(this);
844 Branch(SmiEqual(dividend, SmiConstant(0)), &dividend_is_zero,
845 &dividend_is_not_zero);
846
847 BIND(&dividend_is_zero);
848 {
849 GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
850 Goto(&dividend_is_not_zero);
851 }
852 BIND(&dividend_is_not_zero);
853
854 Node* untagged_divisor = SmiToInt32(divisor);
855 Node* untagged_dividend = SmiToInt32(dividend);
856
857 // Do floating point division if {dividend} is kMinInt (or kMinInt - 1
858 // if the Smi size is 31) and {divisor} is -1.
859 Label divisor_is_minus_one(this), divisor_is_not_minus_one(this);
860 Branch(Word32Equal(untagged_divisor, Int32Constant(-1)),
861 &divisor_is_minus_one, &divisor_is_not_minus_one);
862
863 BIND(&divisor_is_minus_one);
864 {
865 GotoIf(Word32Equal(
866 untagged_dividend,
867 Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))),
868 &bailout);
869 Goto(&divisor_is_not_minus_one);
870 }
871 BIND(&divisor_is_not_minus_one);
872
873 // TODO(epertoso): consider adding a machine instruction that returns
874 // both the result and the remainder.
875 Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor);
876 Node* truncated = Int32Mul(untagged_result, untagged_divisor);
877 // Do floating point division if the remainder is not 0.
878 GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout);
879 Return(SmiFromInt32(untagged_result));
880
881 // Bailout: convert {dividend} and {divisor} to double and do double
882 // division.
883 BIND(&bailout);
884 {
885 var_left_double.Bind(SmiToFloat64(dividend));
886 var_right_double.Bind(SmiToFloat64(divisor));
887 Goto(&do_double_div);
888 }
889 }
890
891 BIND(&do_double_div);
892 {
893 Node* value = Float64Div(var_left_double.value(), var_right_double.value());
894 Return(AllocateHeapNumberWithValue(value));
895 }
896
897 BIND(&do_bigint_div);
898 {
899 Node* context = Parameter(Descriptor::kContext);
900 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
901 var_right.value(), SmiConstant(Operation::kDivide)));
902 }
903}
904
905TF_BUILTIN(Modulus, NumberBuiltinsAssembler) {
906 VARIABLE(var_left, MachineRepresentation::kTagged);
907 VARIABLE(var_right, MachineRepresentation::kTagged);
908 VARIABLE(var_left_double, MachineRepresentation::kFloat64);
909 VARIABLE(var_right_double, MachineRepresentation::kFloat64);
910 Label do_smi_mod(this), do_double_mod(this), do_bigint_mod(this);
911
912 BinaryOp<Descriptor>(&do_smi_mod, &var_left, &var_right, &do_double_mod,
913 &var_left_double, &var_right_double, &do_bigint_mod);
914
915 BIND(&do_smi_mod);
916 Return(SmiMod(CAST(var_left.value()), CAST(var_right.value())));
917
918 BIND(&do_double_mod);
919 Node* value = Float64Mod(var_left_double.value(), var_right_double.value());
920 Return(AllocateHeapNumberWithValue(value));
921
922 BIND(&do_bigint_mod);
923 {
924 Node* context = Parameter(Descriptor::kContext);
925 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
926 var_right.value(), SmiConstant(Operation::kModulus)));
927 }
928}
929
930TF_BUILTIN(Exponentiate, NumberBuiltinsAssembler) {
931 VARIABLE(var_left, MachineRepresentation::kTagged);
932 VARIABLE(var_right, MachineRepresentation::kTagged);
933 Label do_number_exp(this), do_bigint_exp(this);
934 Node* context = Parameter(Descriptor::kContext);
935
936 BinaryOp<Descriptor>(&do_number_exp, &var_left, &var_right, &do_number_exp,
937 nullptr, nullptr, &do_bigint_exp);
938
939 BIND(&do_number_exp);
940 {
941 MathBuiltinsAssembler math_asm(state());
942 Return(math_asm.MathPow(context, var_left.value(), var_right.value()));
943 }
944
945 BIND(&do_bigint_exp);
946 Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
947 var_right.value(), SmiConstant(Operation::kExponentiate)));
948}
949
950TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) {
951 EmitBitwiseOp<Descriptor>(Operation::kShiftLeft);
952}
953
954TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) {
955 EmitBitwiseOp<Descriptor>(Operation::kShiftRight);
956}
957
958TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) {
959 EmitBitwiseOp<Descriptor>(Operation::kShiftRightLogical);
960}
961
962TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) {
963 EmitBitwiseOp<Descriptor>(Operation::kBitwiseAnd);
964}
965
966TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) {
967 EmitBitwiseOp<Descriptor>(Operation::kBitwiseOr);
968}
969
970TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
971 EmitBitwiseOp<Descriptor>(Operation::kBitwiseXor);
972}
973
974TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
975 RelationalComparisonBuiltin<Descriptor>(Operation::kLessThan);
976}
977
978TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) {
979 RelationalComparisonBuiltin<Descriptor>(Operation::kLessThanOrEqual);
980}
981
982TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) {
983 RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThan);
984}
985
986TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) {
987 RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThanOrEqual);
988}
989
990TF_BUILTIN(Equal, CodeStubAssembler) {
991 Node* lhs = Parameter(Descriptor::kLeft);
992 Node* rhs = Parameter(Descriptor::kRight);
993 Node* context = Parameter(Descriptor::kContext);
994
995 Return(Equal(lhs, rhs, context));
996}
997
998TF_BUILTIN(StrictEqual, CodeStubAssembler) {
999 Node* lhs = Parameter(Descriptor::kLeft);
1000 Node* rhs = Parameter(Descriptor::kRight);
1001
1002 Return(StrictEqual(lhs, rhs));
1003}
1004
1005} // namespace internal
1006} // namespace v8
1007