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