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