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