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